JUC包核心源码

1、AQS

AQS图像化展示

AQS的推理以及源码解释

所以研究其子类,只需要看其对state的不同运用,和获取锁成功tryAcquire和释放锁tryRelease。

2、AtomicXXX

AtomicXXX:其实现逻辑就是通过compareAndSwap方法,底层通过cmpxchg汇编指令自动加Lock前缀来保证原子操作。

Lock前缀:Intel CPU中,lock前缀会锁定缓存行或者总线,并且会刷新storeBuffer。保证原子性、可见性。抽象内存模型

Striped64:分槽位,将竞争拆散到不同的Cell上。在sum时循环遍历所有Cell,将其值value相加即可。

3、ReentrantLock

state:

获得锁的次数,非0表示没有线程持有锁。

tryAcquire:

tryRelease:

4、ReentrantReadWriteLock

state:

高16位表示读锁及重入数量(线程重入数量使用Threadlocal),低16位表示写锁及锁重入数量。

ReentrantReadWriteLock.WriteLock

tryAcquire

同一线程持有读锁时,必须先释放读锁,才能再获取写锁

tryRelease

ReentrantReadWriteLock.ReadLock

tryAcquireShared

同一线程持有写锁时,可以直接获取读锁。非公平下,如果当前是读锁,同时一大批读锁过来会导致写操作很难执行(写饥饿)。

tryReleaseShared

5、CountDownLatch

使用共享锁实现。主要作用是,其他线程控制await线程从阻塞变为运行。

state:

表示允许多少个线程获取锁。

6、CyclicBarrier

使用ReentrantLock+Condition实现。Condition原理

将等待的线程全部主要作用是,线程等待一起执行。count记录当前阻塞数量,parties记录需要的个数。

dowait

7、Phaser

state:

64位,分为2个32位

高32位:最高位标记是否结束 其他位表示phase阶段数

低32位:分为2个16位,高16位表示当前参与数,低16位表示未到达数

结束:最高位为1,也即state<0,

异常(空参与):低32位==1,即参与数为0.但是等待数却为1,当phase大于最大值时,会设置此状态

底层逻辑就是cas设置state。new Phaser(fatherphaser)场景比喻:公司准备了多个部门party,各个部门party都有人数要求。当所有部门都准备好开始时公司需要发表讲话之后各部门才能开始。此时就需要父类来统筹管理所有的子类。

phaser.register();

添加参与者,如果在添加时发现party已经开始了,就需要加入新的队列中,所以需要两个队列来保存等待的线程。

phaser.internalAwaitAdvance()

协助唤醒前一个阶段的节点。生成node节点,加入队列中。奇偶两个队列。

phaser.arriveAndAwaitAdvance():

通知已到达。如果当前未到达数大于0,则去阻塞等待;否则回调onAdvance方法,看是否继续执行。设置下一阶段值并设置state,唤醒等待的线程。

phaser.arriveAndDeregister():

将当前线程移除,并且通知已到达。设置参与数-1,等待数-1,如果当前等待数为1,设置循环数+1,和其他位。唤醒其他等待线程

8、StampedLock

state:

低7位:读锁个数;第8:写锁

其他位:版本号,每使用一次写锁,就会+1

优化ReentrantReadWriteLock,使得读读、读写可并行。

tryOptimisticRead

获取乐观读锁,写标志位如果未设置,直接返回版本号;

validate

返回true表示,在获取版本号stamp期间,没有写锁。

loadFence:读屏障。

可以看到Intel x86平台下,jvm实现:使用了发布订阅API中的订阅方法acquire,只是调用了编译器屏障,因为此CPU不存在invalidQueue,使用嗅探技术,不存在读读、写读、写写乱序的问题。内存屏障

readLock

第一层for循环:

unlockRead

writeLock

只有没有写锁或者读锁时,才允许抢写锁。可以发现其实和ReentrantReadWriteLock.WriteLock差不多,都是排他锁。

unlockWrite

可以发现写锁不允许重入。

release

9、Exchanger