转自:http://brokendreams.iteye.com/blog/2250109

功能简介:
  • 原子量和普通变量相比,主要体现在读写的线程安全上。对原子量的是原子的(比如多线程下的共享变量i++就不是原子的),由CAS操作保证原子性。对原子量的读可以读到最新值,由volatile关键字来保证可见性
  • 原子量多用于数据统计(如接口调用次数)、一些序列生成(多线程环境下)以及一些同步数据结构中。
源码分析:
  • 首先,原子量的一些较底层的操作都是来自sun.misc.Unsafe类,所以原子量内部有一个Unsafe的静态引用。
  1. private static final Unsafe unsafe = Unsafe.getUnsafe();
       在openJdk代码中可以找到这个类,目录openJdk的jdk/share/classes/sun/misc/。
       这个类里面大多数方法都是native的,方法实现可以在openJdk的hotspot/share/vm/prims/unsafe.cpp里面找到。 
  • 接下来,先看下AtomicInteger的源码。
       在AtomicInteger源码中,由内部的一个int域来保存值:
  1. private volatile int value;
       注意到这个int域由volatile关键字修饰,可以保证可见性。
       细节:volatile怎么保证可见性呢?对于被 volatile修饰的域来说,对域进行的写入操作,在指令层面会在必要的时候(多核CPU)加入内存屏障(如:lock addl $0x0),这个内存屏障的作用是令本次写操作刷回主存,同时使其他CPU的cacheline中相应数据失效。所以当其他CPU需要访问相应数据的时 候,会到主存中访问,从而保证了多线程环境下相应域的可见性。
       接下来看一下CAS操作,AtomicInteger中的CAS操作体现在方法compareAndSet。它的实现在unsafe.cpp里面:
  1. /*
  2. * Implementation of class sun.misc.Unsafe
  3. */
  4. ...
  5. UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  6. UnsafeWrapper("Unsafe_CompareAndSwapInt");
  7. oop p = JNIHandles::resolve(obj);
  8. jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  9. return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
  10. UNSAFE_END

这里调用了Atomic的cmpxchg方法,继续找一下。这个方法定义在hotspot/share/vm/runtime/atomic.hpp 中,实现在hotspot/share/vm/runtime/atomic.cpp中,最终实现取决于底层OS,比如linux x86,实现内联在hotspot部分代码os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp:

  1. // Adding a lock prefix to an instruction on MP machine
  2. #define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
  3. ...
  4. inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
  5. int mp = os::is_MP();
  6. __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
  7. : "=a" (exchange_value)
  8. : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
  9. : "cc", "memory");
  10. return exchange_value;
  11. }
       从上面的代码中可以看到,如果是CPU是多核(multi processors)的话,会添加一个lock;前缀,这个lock;前缀也是内存屏障,它的作用是在执行后面指令的过程中锁总线(或者是锁 cacheline),保证一致性。后面的指令cmpxchgl就是x86的比较并交换指令了。
       接下来有一个和compareAndSet类似的方法,weakCompareAndSet。
       从注释看,这个方法会发生fail spuriously(伪失败),而且不保证(指令)顺序,只能在一些特性场景(一些计数和统计)下替换compareAndSet。但从方法实现上看和compareAndSet没什么区别:
  1. /**
  2. * Atomically sets the value to the given updated value
  3. * if the current value {@code ==} the expected value.
  4. *
  5. * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
  6. * and does not provide ordering guarantees, so is only rarely an
  7. * appropriate alternative to {@code compareAndSet}.
  8. *
  9. * @param expect the expected value
  10. * @param update the new value
  11. * @return true if successful.
  12. */
  13. public final boolean weakCompareAndSet(int expect, int update) {
  14. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  15. }
       但还是应该按照API的说明来使用这两个方法,以防未来方法内部(实现或者底层内部机制)发生变化。
       其余的大多数方法都是基于compareAndSet方法来实现的,来看其中一个,incrementAndGet方法:
  1. /**
  2. * Atomically increments by one the current value.
  3. *
  4. * @return the updated value
  5. */
  6. public final int incrementAndGet() {
  7. for (;;) {
  8. int current = get();
  9. int next = current + 1;
  10. if (compareAndSet(current, next))
  11. return next;
  12. }
  13. }
  14.  
       这个方法也体现了CAS一般是使用风格-CAS Loop,不断重试,直到成功。
       当然,这也不是绝对的,JVM底层完全可以用更好的方式也替换这些方法,比如使用内联的lock;xadd就会比cmpxchg指令更好一些。

最后,AtomicInteger还有一个方法,lazySet:

  1. /**
  2. * Eventually sets to the given value.
  3. *
  4. * @param newValue the new value
  5. * @since 1.6
  6. */
  7. public final void lazySet(int newValue) {
  8. unsafe.putOrderedInt(this, valueOffset, newValue);
  9. }
       看下unsafe.cpp中putOrderedInt方法的实现:
  1. {CC"putOrderedInt", CC"("OBJ"JI)V", FN_PTR(Unsafe_SetOrderedInt)},
  2. ...
  3. // The non-intrinsified versions of setOrdered just use setVolatile
  4. UNSAFE_ENTRY(void, Unsafe_SetOrderedInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint x))
  5. UnsafeWrapper("Unsafe_SetOrderedInt");
  6. SET_FIELD_VOLATILE(obj, offset, jint, x);
  7. UNSAFE_END

注:上面有句注释,说明这只是一个非内联的setOrdered方法的实现,使用了setVolatile(和setVolatile一样的效果)。

       其中,SET_FIELD_VOLATILE的定义如下:
  1. #define SET_FIELD_VOLATILE(obj, offset, type_name, x) \
  2. oop p = JNIHandles::resolve(obj); \
  3. OrderAccess::release_store_fence((volatile type_name*)index_oop_from_field_offset_long(p, offset), x);
       在hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp找到了这个方法 的内联实现。(hotspot/src/share/vm/runtime/orderAccess.hpp这个头文件里面的注释值得留意一下):
  1. inline void OrderAccess::release_store_fence(volatile jint* p, jint v) {
  2. __asm__ volatile ( "xchgl (%2),%0"
  3. : "=r" (v)
  4. : "0" (v), "r" (p)
  5. : "memory");
  6. }
       可见,这里是通过xchgl这个指令来实现的setOrdered。
       但是,上面只是非内联的实现,我们看下内联的实现是什么样的。
       在hotspot/src/share/vm/classfile/vmSymbols.hpp中有如下代码:
  1. do_intrinsic(_putOrderedInt, sun_misc_Unsafe,putOrderedInt_name, putOrderedInt_signature,F_RN)

然后找到hotspot/src/share/vm/opto/library_call.cpp中找到相应实现:

  1. case vmIntrinsics::_putOrderedInt:
  2. return inline_unsafe_ordered_store(T_INT);
  3. ...
  4. bool LibraryCallKit::inline_unsafe_ordered_store(BasicType type) {
  5. // This is another variant of inline_unsafe_access, differing in
  6. // that it always issues store-store ("release") barrier and ensures
  7. // store-atomicity (which only matters for "long").
  8. if (callee()->is_static()) return false; // caller must have the capability!
  9. ...//省略不重要的部分
  10. insert_mem_bar(Op_MemBarRelease);
  11. insert_mem_bar(Op_MemBarCPUOrder);
  12. // Ensure that the store is atomic for longs:
  13. bool require_atomic_access = true;
  14. Node* store;
  15. if (type == T_OBJECT) // reference stores need a store barrier.
  16. store = store_oop_to_unknown(control(), base, adr, adr_type, val, type);
  17. else {
  18. store = store_to_memory(control(), adr, val, type, adr_type, require_atomic_access);
  19. }
  20. insert_mem_bar(Op_MemBarCPUOrder);
  21. return true;
  22. }
       我们看到这个方法里在保存动作的前后,有3个地方插入了内存屏障。
       再看下hotspot/src/cpu/x86/vm/x86_64.ad:
  1. instruct membar_release() %{
  2. match(MemBarRelease);
  3. ins_cost(400);
  4. size(0);
  5. format %{ "MEMBAR-release ! (empty encoding)" %}
  6. ins_encode( );
  7. ins_pipe(empty);
  8. %}
  9. ...
  10. instruct membar_volatile(eFlagsReg cr) %{
  11. match(MemBarVolatile);
  12. effect(KILL cr);
  13. ins_cost(400);
  14. format %{
  15. $$template
  16. if (os::is_MP()) {
  17. $$emit$$"LOCK ADDL [ESP + #0], 0\t! membar_volatile"
  18. } else {
  19. $$emit$$"MEMBAR-volatile ! (empty encoding)"
  20. }
  21. %}
  22. ins_encode %{
  23. __ membar(Assembler::StoreLoad);
  24. %}
  25. ins_pipe(pipe_slow);
  26. %}
       可见,除了membar_volatile中会添加LOCK ADDL这些指令,其他的貌似没什么卵用。 所以上面那3个insert_mem_bar也相当于没有加任何内存屏障。在这种情况下,lazySet就相当于对一个普通域的写操作喽。
  • 再看下AtomicBoolean的源码。
       AtomicBoolean内部是用一个int域来表示布尔状态,1表示true;0表示false:
  1. private volatile int value;
  2. /**
  3. * Creates a new {@code AtomicBoolean} with the given initial value.
  4. *
  5. * @param initialValue the initial value
  6. */
  7. public AtomicBoolean(boolean initialValue) {
  8. value = initialValue ? 1 : 0;
  9. }
        CAS、lazySet方法也都分别调用unsafe的compareAndSwapInt和putOrderedInt,上面分析过了。
  • 继续看下AtomicLong的源码。
       AtomicLong内部是用一个long域来保存值:
  1. /**
  2. * Records whether the underlying JVM supports lockless
  3. * compareAndSwap for longs. While the Unsafe.compareAndSwapLong
  4. * method works in either case, some constructions should be
  5. * handled at Java level to avoid locking user-visible locks.
  6. */
  7. static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
  8. /**
  9. * Returns whether underlying JVM supports lockless CompareAndSet
  10. * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
  11. */
  12. private static native boolean VMSupportsCS8();
  13. static {
  14. try {
  15. valueOffset = unsafe.objectFieldOffset
  16. (AtomicLong.class.getDeclaredField("value"));
  17. } catch (Exception ex) { throw new Error(ex); }
  18. }
  19. private volatile long value;
       这里注意到,AtomicLong中还提供了一个包内可见的静态域VM_SUPPORTS_LONG_CAS来表示底层是否支持Long类型(8字节)的lockless CAS操作。
       AtomicLong内部整体结构和AtomicInteger类似,主要来看下内部使用的unsafe的方法有什么不同,首先CAS操作使用了unsafe的compareAndSwapLong方法:
  1. /**
  2. * Atomically sets the value to the given updated value
  3. * if the current value {@code ==} the expected value.
  4. *
  5. * @param expect the expected value
  6. * @param update the new value
  7. * @return true if successful. False return indicates that
  8. * the actual value was not equal to the expected value.
  9. */
  10. public final boolean compareAndSet(long expect, long update) {
  11. return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
  12. }
       在unsafe.cpp中找到实现:
  1. {CC"compareAndSwapLong", CC"("OBJ"J""J""J"")Z", FN_PTR(Unsafe_CompareAndSwapLong)},
  2.  
  3. ...
  4. UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x))
  5. UnsafeWrapper("Unsafe_CompareAndSwapLong");
  6. Handle p (THREAD, JNIHandles::resolve(obj));
  7. jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
  8. if (VM_Version::supports_cx8())
  9. return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;
  10. else {
  11. jboolean success = false;
  12. ObjectLocker ol(p, THREAD);
  13. if (*addr == e) { *addr = x; success = true; }
  14. return success;
  15. }
  16. UNSAFE_END

从实现中可以看到,如果平台不支持8字节的CAS操作,就会加锁然后进行设置操作;如果支持,就会调用Atomic::cmpxchg方法,方法实现可以 参考具体平台内联代码hotspot/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp:

  1. // Adding a lock prefix to an instruction on MP machine
  2. #define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
  3. inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) {
  4. bool mp = os::is_MP();
  5. __asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)"
  6. : "=a" (exchange_value)
  7. : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
  8. : "cc", "memory");
  9. return exchange_value;
  10. }
       其实就是(多核情况下带lock前缀的)cmpxchgq指令。
       然后看一下lazySet中使用到的unsafe的putOrderedLong方法。
  1. /**
  2. * Eventually sets to the given value.
  3. *
  4. * @param newValue the new value
  5. * @since 1.6
  6. */
  7. public final void lazySet(long newValue) {
  8. unsafe.putOrderedLong(this, valueOffset, newValue);
  9. }
  10.  
       同样在unsafe.cpp中可以找到该方法的实现:
  1. {CC"putOrderedLong",     CC"("OBJ"JJ)V",             FN_PTR(Unsafe_SetOrderedLong)},
  2. ...
  3. UNSAFE_ENTRY(void, Unsafe_SetOrderedLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong x))
  4. UnsafeWrapper("Unsafe_SetOrderedLong");
  5. #if defined(SPARC) || defined(X86)
  6. // Sparc and X86 have atomic jlong (8 bytes) instructions
  7. SET_FIELD_VOLATILE(obj, offset, jlong, x);
  8. #else
  9. // Keep old code for platforms which may not have atomic long (8 bytes) instructions
  10. {
  11. if (VM_Version::supports_cx8()) {
  12. SET_FIELD_VOLATILE(obj, offset, jlong, x);
  13. }
  14. else {
  15. Handle p (THREAD, JNIHandles::resolve(obj));
  16. jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
  17. ObjectLocker ol(p, THREAD);
  18. *addr = x;
  19. }
  20. }
  21. #endif
  22. UNSAFE_END

从实现上看,如果平台是SPARC或者X86或者平台支持8字节CAS,就相当于执行了一个volatile write;否则,加锁写。

       当然还要看一下内联方法,在hotspot/src/share/vm/classfile/vmSymbols.hpp中有如下代码:
  1. do_intrinsic(_putOrderedLong,           sun_misc_Unsafe,        putOrderedLong_name, putOrderedLong_signature, F_RN)  \

然后找到hotspot/src/share/vm/opto/library_call.cpp中找到相应实现。

  1. case vmIntrinsics::_putOrderedLong:
  2. return inline_unsafe_ordered_store(T_LONG);
  3. ...
  4. ol LibraryCallKit::inline_unsafe_ordered_store(BasicType type) {
  5. // This is another variant of inline_unsafe_access, differing in
  6. // that it always issues store-store ("release") barrier and ensures
  7. // store-atomicity (which only matters for "long").
  8. if (callee()->is_static())  return false;  // caller must have the capability!
  9. ...//忽略不重要部分
  10. // Ensure that the store is atomic for longs:
  11. bool require_atomic_access = true;
  12. Node* store;
  13. if (type == T_OBJECT) // reference stores need a store barrier.
  14. store = store_oop_to_unknown(control(), base, adr, adr_type, val, type);
  15. else {
  16. store = store_to_memory(control(), adr, val, type, adr_type, require_atomic_access);
  17. }
  18. insert_mem_bar(Op_MemBarCPUOrder);
  19. return true;

从代码的注释上可以发现,require_atomic_access设置为true,为了保证long写操作的原子性。继续跟代码,找到hotspot/src/share/vm/opto/graphKit.cpp:

  1. Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt,
  2. int adr_idx,
  3. bool require_atomic_access) {
  4. assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" );
  5. const TypePtr* adr_type = NULL;
  6. debug_only(adr_type = C->get_adr_type(adr_idx));
  7. Node *mem = memory(adr_idx);
  8. Node* st;
  9. if (require_atomic_access && bt == T_LONG) {
  10. st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val);
  11. } else {
  12. st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt);
  13. }
  14. st = _gvn.transform(st);
  15. set_memory(st, adr_idx);
  16. // Back-to-back stores can only remove intermediate store with DU info
  17. // so push on worklist for optimizer.
  18. if (mem->req() > MemNode::Address && adr == mem->in(MemNode::Address))
  19. record_for_igvn(st);
  20. return st;
  21. }

可见,这里会针对long做原子写操作(这里的原子操作应该指的是将long的高4字节和低4字节的操作合并成一个原子操作,比如某些平台不支持非volatile的long/double域的原子操作)。

 
 
  • 最后看下AtomicReference的源码。
       AtomicReference内部构成和其他原子量基本一致,区别只是这个类内部保存一个对象引用。
  1. public class AtomicReference<V>  implements java.io.Serializable {
  2. private static final long serialVersionUID = -1848883965231344442L;
  3. private static final Unsafe unsafe = Unsafe.getUnsafe();
  4. private static final long valueOffset;
  5. static {
  6. try {
  7. valueOffset = unsafe.objectFieldOffset
  8. (AtomicReference.class.getDeclaredField("value"));
  9. } catch (Exception ex) { throw new Error(ex); }
  10. }
  11. private volatile V value;

重点看一下内部调用的unsafe的compareAndSwapObject和putOrderedObject方法,先看一下compareAndSwapObject:

  1. /**
  2. * Atomically sets the value to the given updated value
  3. * if the current value {@code ==} the expected value.
  4. * @param expect the expected value
  5. * @param update the new value
  6. * @return true if successful. False return indicates that
  7. * the actual value was not equal to the expected value.
  8. */
  9. public final boolean compareAndSet(V expect, V update) {
  10. return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
  11. }

在unsafe.cpp中可以找到实现:

  1. {CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z",  FN_PTR(Unsafe_CompareAndSwapObject)},
  2. ...
  3. UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h))
  4. UnsafeWrapper("Unsafe_CompareAndSwapObject");
  5. oop x = JNIHandles::resolve(x_h);
  6. oop e = JNIHandles::resolve(e_h);
  7. oop p = JNIHandles::resolve(obj);
  8. HeapWord* addr = (HeapWord *)index_oop_from_field_offset_long(p, offset);
  9. if (UseCompressedOops) {
  10. update_barrier_set_pre((narrowOop*)addr, e);
  11. } else {
  12. update_barrier_set_pre((oop*)addr, e);
  13. }
  14. oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e);
  15. jboolean success  = (res == e);
  16. if (success)
  17. update_barrier_set((void*)addr, x);
  18. return success;
  19. UNSAFE_END

可以看到里面实际上是执行oopDesc::atomic_compare_exchange_oop这个方法。找到hotspot/src/share/vm/oops/oop.inline.hpp中该方法实现:

  1. inline oop oopDesc::atomic_compare_exchange_oop(oop exchange_value,
  2. volatile HeapWord *dest,
  3. oop compare_value) {
  4. if (UseCompressedOops) {
  5. // encode exchange and compare value from oop to T
  6. narrowOop val = encode_heap_oop(exchange_value);
  7. narrowOop cmp = encode_heap_oop(compare_value);
  8. narrowOop old = (narrowOop) Atomic::cmpxchg(val, (narrowOop*)dest, cmp);
  9. // decode old from T to oop
  10. return decode_heap_oop(old);
  11. } else {
  12. return (oop)Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value);
  13. }
  14. }

cmpxchg之前分析过,看看cmpxche_ptr,找到hotspot/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp中实现:

  1. inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) {
  2. return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value);
  3. }
  4. inline jlong    Atomic::cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value) {
  5. bool mp = os::is_MP();
  6. __asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)"
  7. : "=a" (exchange_value)
  8. : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
  9. : "cc", "memory");
  10. return exchange_value;
  11. }

就是(多核下带lock前缀的)cmpxchgq命令了。

       putOrderedObject方法按之前几篇的查找方法,会发现内联之后,相当于一个普通写操作了。
 
       OK,源码分析到此结束!
  

Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX的更多相关文章

  1. Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue

    功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...

  2. Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer

    功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...

  3. Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue

    功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...

  4. Jdk1.6 JUC源码解析(7)-locks-ReentrantLock

    功能简介: Java代码层面提供的锁机制,可做为Synchronized(jvm内置)的替代物,和Synchronized一样都是可重入的. 与Synchronized相比较而言,ReentrantL ...

  5. 【JUC源码解析】ScheduledThreadPoolExecutor

    简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...

  6. 【JUC源码解析】SynchronousQueue

    简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...

  7. 【JUC源码解析】ForkJoinPool

    简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...

  8. 【JUC源码解析】DelayQueue

    简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...

  9. 【JUC源码解析】CyclicBarrier

    简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...

随机推荐

  1. JDK中日期和时间的几个常用类浅析(四)

    java.time.Instant   java.time.Instant类对应的是时间线上的一个时间点.该类通过保存着从格林威治的起始时间(1970年一月一日零点零分)开始计算所经过的纳妙数来表示时 ...

  2. python之smtplib发邮件

    第一版: 认证发信,不支持附件 #!/usr/bin/env python # --------------------------------------- # author : Geng Jie ...

  3. Access中的自定义排序设置方式

    一.问题起因 最近有网友提问说,Access中在用查询指定排序方式时,为什么只有升序跟降序,怎么米有自定义排序了?竟然比Excel都弱啊! 其实这是对Access的误解,我这就给大家来解一下疑惑.案例 ...

  4. Excel公式-求最低价网站名字

    p{ font-size: 15px; } .alexrootdiv>div{ background: #eeeeee; border: 1px solid #aaa; width: 99%; ...

  5. 老李分享:DBA

    poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨询电话010-845052 ...

  6. Fragment深入解析

    写在顶部表示这点很重要:  本文转载自博客:http://blog.csdn.net/lmj623565791/article/details/37970961   欢迎访问原文 自从Fragment ...

  7. DataTables源码分析(一)

    DataTables源码分析 写在前面 作为一名常年奋战在java世界中的程序猿,当我接触到现在所谓的前端技术时,内心其实是崩溃的.因为,前端的技术给我的第一个感觉就是"乱",这里 ...

  8. Maven工程webinfo下面的JSP页面无法加载.js、.css文件的解决方案

    --下面是我的工程路径 --我jsp的写法 -----启动工程,访问js文件的路径是这样的, href="http://localhost:8080/activiti/css/public. ...

  9. 关于mysql的初步学习

    1.在windows上使用CMD链接数据库 这是原始用户表 users 这是通过 语句插入而来的 user表和user2表结构相同 user2 的数据 通过如下SQL语句从users表赋值过来: in ...

  10. es6基础系列二:Number

    es6中关于Number类型的变化,主要有以下几点 Number.isInteger(新增,判断是否为整数) Number.EPSILON(新增,判断是否可忽略的误差) Number.MAX_SAFE ...