目录
- 1. 是什么
- 1.1. synchronized vs ReentranLock
- 2. 实现原理
- 3. 公平锁
- 3.1. 如何使用
- 3.2. 原理分析
- 3.2.1. 构造方法
- 3.2.2. 加锁
- 3.2.2.1. 调用公平锁的lock方法
- 3.2.2.2. 调用AQS的acquire方法获取锁
- 3.2.2.3. 尝试获取锁【只有队头才允许抢占锁--公平锁】
- 3.2.2.4. 尝试获取锁失败加入阻塞队列
- 3.2.2.5. 阻塞,等待唤醒继续获取锁
- 3.2.3. 解锁
- 3.2.3.1. 使用AQS释放锁
- 3.2.3.2. 尝试释放锁
- 3.2.3.3. 释放锁成功后唤醒阻塞队列中的节点
- 4. 非公平锁
- 4.1. 如何使用
- 4.2. 实现原理
- 4.2.1. 构造方法
- 4.2.2. 加锁
- 4.2.2.1. 使用非公平锁加锁
- 4.2.2.2. 通过AQS加锁
- 4.2.2.3. 通过非公平锁尝试加锁
- 4.2.2.3.1. 非公平锁尝试加锁的操作【不管是否队头都可以抢占锁--非公平锁】
- 4.2.2.4. 尝试加锁失败,加入阻塞队列
- 4.2.3. 解锁
- 4.2.3.1. 使用AQS释放锁
- 4.2.3.2. 尝试释放锁
- 4.2.3.2.1. 释放锁成功后唤醒阻塞队列中的后续节点
- 5. 参考
1. 是什么
在jdk5之前,synchronized效率极低,于是写了ReentranLock代替。
后来jdk7优化了synchronized,参考Java源码分析系列笔记-2.锁的优化 - ThinkerQAQ - 博客园。两者性能区别不大
1.1. synchronized vs ReentranLock
比较SynchronizedReentrantLock等待结合object wait/notify结合condition await/signal使用难度简单。jvm会处理加锁,解锁的过程麻烦。需要手动lock、unlock,且unlock得放在finally块中特性可重入 不可中断 非公平可重入 可中断 可公平实现原理monitorAQS2. 实现原理
2.1. uml图
由uml图可以看出ReentranLock底层是用AQS实现的,有一个Sync属性(继承AQS类),如果是非公平锁则用的NonfairSync实现类,否则用的FairSync类
具体的实现参考
3. 公平锁
所谓公平锁,遵循先到先得的原则。
即使锁已经被释放了,后到的也不能去抢占锁,得等到前面没人时才能去获取
3.1. 如何使用
- public class TestReentrantLock
- {
- private static int val = 0;
- private final static Lock lock = new ReentrantLock(true);//公平锁
- public static void main(String[] args) throws InterruptedException
- {
- Thread thread1 = new Thread(() -> {
- for (int i = 0; i < 100000; i++)
- {
- try
- {
- lock.lock();
- val++;
- }
- finally
- {
- lock.unlock();
- }
- }
- });
- Thread thread2 = new Thread(() -> {
- for (int i = 0; i < 100000; i++)
- {
- try
- {
- lock.lock();
- val--;
- }
- finally
- {
- lock.unlock();
- }
- }
- });
- thread1.start();
- thread2.start();
- thread1.join();
- thread2.join();
- System.out.println(val);
- }
- }
复制代码 3.2. 原理分析
3.2.1. 构造方法
3.2.1.1. 底层使用AQS实现
- public class ReentrantLock implements Lock, java.io.Serializable {
- private final Sync sync;
- //默认非公平锁
- public ReentrantLock() {
- sync = new NonfairSync();
- }
- public ReentrantLock(boolean fair) {
- //true的话,公平锁使用FairSync,否则是NonfairSync
- sync = fair ? new FairSync() : new NonfairSync();
- }
- //Sync是AQS的子类
- abstract static class Sync extends AbstractQueuedSynchronizer {}
- //FairSync是Sync的子类
- static final class FairSync extends Sync {}
- }
复制代码 3.2.2. 加锁
- public void lock() {
- //调用FairSync的lock
- sync.lock();
- }
复制代码 3.2.2.1. 调用公平锁的lock方法
- final void lock() {
- //调用AQS的acquire
- acquire(1);
- }
复制代码 3.2.2.2. 调用AQS的acquire方法获取锁
- public final void acquire(int arg) {
- //调用FairSync的tryAcquire获取锁
- if (!tryAcquire(arg) &&
- //获取锁失败加入AQS队列。并且死循环阻塞当前线程,等待唤醒继续获取锁
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- //恢复中断标记
- selfInterrupt();
- }
复制代码 由于FairSync重写了AQS的tryAcquire方法,因此这里会调用FairSync的tryAcquire
其他的逻辑同5.AQS.md,下面只是简要说一下主要逻辑
3.2.2.3. 尝试获取锁【只有队头才允许抢占锁--公平锁】
- protected final boolean tryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- //锁尚未被获取
- if (c == 0) {
- //【公平锁】:队列中我的前面没人等待锁(队列为空或者我就是队列的队头)
- if (!hasQueuedPredecessors() &&
- //CAS设置state获取锁成功
- compareAndSetState(0, acquires)) {
- //设置持有锁的线程为当前线程
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- //锁已经被获取,且是当前线程,那么重入
- else if (current == getExclusiveOwnerThread()) {
- //增加state量
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- //获取锁失败返回false
- return false;
- }
复制代码 3.2.2.4. 尝试获取锁失败加入阻塞队列
- private Node addWaiter(Node mode) {
- //用当前线程、EXCLUSIVE模式构造节点
- Node node = new Node(Thread.currentThread(), mode);
- // 队列不为空
- Node pred = tail;
- if (pred != null) {
- //插入到队尾
- node.prev = pred;
- if (compareAndSetTail(pred, node)) {
- pred.next = node;
- return node;
- }
- }
- //队列为空或者插入到队尾失败
- enq(node);
- return node;
- }
复制代码 3.2.2.4.1. 入队的操作
- private Node enq(final Node node) {
- //死循环直到入队成功
- for (;;) {
- Node t = tail;
- //队列为空,那么初始化头节点。注意是new Node而不是当前node(即队头是个占位符)
- if (t == null) {
- if (compareAndSetHead(new Node()))
- tail = head;
- //队列不为空,插入到队尾
- } else {
- node.prev = t;
- if (compareAndSetTail(t, node)) {
- t.next = node;
- return t;
- }
- }
- }
- }
复制代码 3.2.2.5. 阻塞,等待唤醒继续获取锁
- final boolean acquireQueued(final Node node, int arg) {
- boolean failed = true;
- try {
- boolean interrupted = false;
- //死循环直到获取锁成功
- for (;;) {
- //逻辑1.
- //当前节点的前一个节点时头节点的时候(公平锁:即我的前面没有人等待获取锁),尝试获取锁
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- //获取锁成功后设置头节点为当前节点
- setHead(node);
- p.next = null; // help GC
- failed = false;
- return interrupted;
- }
- //逻辑2.
- //当前节点的前一个节点状态时SIGNAL(承诺唤醒当前节点)的时候,阻塞当前线程。
- //什么时候唤醒?释放锁的时候
- //唤醒之后干什么?继续死循环执行上面的逻辑1
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- interrupted = true;
- }
- } finally {
- //何时执行这段逻辑?发生异常导致获取锁失败的时候
- if (failed)
- cancelAcquire(node);
- }
- }
复制代码 3.2.2.5.1. 判断是否需要阻塞
- shouldParkAfterFailedAcquire
- //根据(前一个节点,当前节点)->是否阻塞当前线程
- private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
- int ws = pred.waitStatus;
- //前一个节点的状态时SIGNAL,即释放锁后承诺唤醒当前节点,那么返回true可以阻塞当前线程
- if (ws == Node.SIGNAL)
- return true;
- //前一个节点状态>0,即CANCEL。
- //那么往前遍历找到没有取消的前置节点。同时从链表中移除CANCEL状态的节点
- if (ws > 0) {
- do {
- node.prev = pred = pred.prev;
- } while (pred.waitStatus > 0);
- pred.next = node;
- // 前置节点状态>=0,即0或者propagate。
- //这里通过CAS把前置节点状态改成signal成功获取锁,失败的话再阻塞。why?
- } else {
- compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
- }
- return false;
- }
复制代码 3.2.2.5.1.1. 阻塞当前线程
- private final boolean parkAndCheckInterrupt() {
- //使用Unsafe阻塞当前线程,这里会清除线程中断的标记,因此需要返回中断的标记
- LockSupport.park(this);
- return Thread.interrupted();
- }
复制代码 3.2.3. 解锁
- public void unlock() {
- //调用AQS的release方法
- sync.release(1);
- }
复制代码 3.2.3.1. 使用AQS释放锁
- public final boolean release(int arg) {
- //Sync重写了调用Sync释放锁成功
- if (tryRelease(arg)) {
- Node h = head;
- //队头不为空且状态正常,那么唤醒头节点
- if (h != null && h.waitStatus != 0)
- unparkSuccessor(h);
- return true;
- }
- return false;
- }
复制代码 Sync重写了tryRelease方法,因此这里调用的是Sync.tryRelease
其他的逻辑同5.AQS.md,下面只是简要说一下主要逻辑
3.2.3.2. 尝试释放锁
- protected final boolean tryRelease(int releases) {
- //解锁
- int c = getState() - releases;
- //加锁解锁必须同一个线程
- if (Thread.currentThread() != getExclusiveOwnerThread())
- throw new IllegalMonitorStateException();
- boolean free = false;
- if (c == 0) {
- //锁全部释放成功后,置占用锁的线程为空
- free = true;
- setExclusiveOwnerThread(null);
- }
- //CAS设置解锁
- setState(c);
- return free;
- }
复制代码 3.2.3.3. 释放锁成功后唤醒阻塞队列中的节点
- private void unparkSuccessor(Node node) {
- int ws = node.waitStatus;
- //当前节点的状态<0,则把状态改为0
- //0是空的状态,因为node这个节点的线程释放了锁后续不需要做任何
- if (ws < 0)
- compareAndSetWaitStatus(node, ws, 0);
- //当前节点的下一个节点为空或者状态>0(即是取消状态)
- Node s = node.next;
- if (s == null || s.waitStatus > 0) {
- s = null;
- //那么从队尾开始往前遍历找到离当前节点最近的下一个状态<=0的节点(即非取消状态)
- for (Node t = tail; t != null && t != node; t = t.prev)
- if (t.waitStatus <= 0)
- s = t;
- }
- //唤醒下一个节点(公平锁)
- if (s != null)
- LockSupport.unpark(s.thread);
- }
复制代码 4.2. 实现原理
4.2.1. 构造方法
- public class TestReentrantLock
- {
- private static int val = 0;
- private final static Lock lock = new ReentrantLock();//非公平锁
- public static void main(String[] args) throws InterruptedException
- {
- Thread thread1 = new Thread(() -> {
- for (int i = 0; i < 100000; i++)
- {
- try
- {
- lock.lock();
- val++;
- }
- finally
- {
- lock.unlock();
- }
- }
- });
- Thread thread2 = new Thread(() -> {
- for (int i = 0; i < 100000; i++)
- {
- try
- {
- lock.lock();
- val--;
- }
- finally
- {
- lock.unlock();
- }
- }
- });
- thread1.start();
- thread2.start();
- thread1.join();
- thread2.join();
- System.out.println(val);
- }
- }
复制代码 4.2.2. 加锁
- public class ReentrantLock implements Lock, java.io.Serializable {
-
- private final Sync sync;
-
- //默认非公平锁
- public ReentrantLock() {
- sync = new NonfairSync();
- }
-
- public ReentrantLock(boolean fair) {
- //true的话,公平锁使用FairSync,否则是NonfairSync
- sync = fair ? new FairSync() : new NonfairSync();
- }
-
- //Sync是AQS的子类
- abstract static class Sync extends AbstractQueuedSynchronizer {}
- //FairSync是Sync的子类
- static final class FairSync extends Sync {}
- }
复制代码 4.2.2.1. 使用非公平锁加锁
- public void lock() {
- //简单得调用Sync属性的lock方法。即NonfairSync的lock方法
- sync.lock();
- }
复制代码 4.2.2.2. 通过AQS加锁
- final void lock() {
- //获取锁。使用CAS设置state的值为1,这里state代表互斥量
- if (compareAndSetState(0, 1))
- //设置当前线程为拥有互斥量的线程
- setExclusiveOwnerThread(Thread.currentThread());
- else
- //获取失败则调用AQS的acquire方法
- acquire(1);
- }
复制代码 由于NonfairSync重写了AQS的tryAcquire方法,因此这里会调用NonfairSync的tryAcquire
其他的逻辑同5.AQS.md,下面只是简要说一下主要逻辑
4.2.2.3. 通过非公平锁尝试加锁
- public final void acquire(int arg) {
- //调用NonFairSync的tryAcquire获取锁
- if (!tryAcquire(arg) &&
- //获取锁失败加入AQS队列。并且死循环阻塞当前线程,等待唤醒继续获取锁
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- //恢复中断标记
- selfInterrupt();
- }
复制代码 4.2.2.3.1. 非公平锁尝试加锁的操作【不管是否队头都可以抢占锁--非公平锁】
- NonfairSync.nonfairTryAcquire
- protected final boolean tryAcquire(int acquires) {
- //调用NonfairSync.nonfairTryAcquire
- return nonfairTryAcquire(acquires);
- }
复制代码 4.2.2.4. 尝试加锁失败,加入阻塞队列
- final boolean nonfairTryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- //锁尚未被获取
- if (c == 0) {
- //不管前面是否有人等待,直接尝试获取锁(非公平锁)
- if (compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- //锁已被获取且时当前线程,重入
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0) // overflow
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
复制代码 4.2.2.4.1. 加入队列的操作
- private Node addWaiter(Node mode) {
- //用当前线程、EXCLUSIVE模式构造节点
- Node node = new Node(Thread.currentThread(), mode);
- // 队列不为空
- Node pred = tail;
- if (pred != null) {
- //插入到队尾
- node.prev = pred;
- if (compareAndSetTail(pred, node)) {
- pred.next = node;
- return node;
- }
- }
- //队列为空或者插入到队尾失败
- enq(node);
- return node;
- }
复制代码- private Node enq(final Node node) {
- //死循环直到入队成功
- for (;;) {
- Node t = tail;
- //队列为空,那么初始化头节点。注意是new Node而不是当前node(即队头是个占位符)
- if (t == null) {
- if (compareAndSetHead(new Node()))
- tail = head;
- //队列不为空,插入到队尾
- } else {
- node.prev = t;
- if (compareAndSetTail(t, node)) {
- t.next = node;
- return t;
- }
- }
- }
- }
复制代码
- shouldParkAfterFailedAcquire
- final boolean acquireQueued(final Node node, int arg) {
- boolean failed = true;
- try {
- boolean interrupted = false;
- //死循环直到获取锁成功
- for (;;) {
- //逻辑1.
- //当前节点的前一个节点时头节点的时候(公平锁:即我的前面没有人等待获取锁),尝试获取锁
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- //获取锁成功后设置头节点为当前节点
- setHead(node);
- p.next = null; // help GC
- failed = false;
- return interrupted;
- }
- //逻辑2.
- //当前节点的前一个节点状态时SIGNAL(承诺唤醒当前节点)的时候,阻塞当前线程。
- //什么时候唤醒?释放锁的时候
- //唤醒之后干什么?继续死循环执行上面的逻辑1
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- interrupted = true;
- }
- //如果发生了异常,那么执行下面的逻辑
- } finally {
- //除了获取锁成功的情况都会执行cancelAcquire方法
- if (failed)
- cancelAcquire(node);
- }
- }
复制代码- //根据(前一个节点,当前节点)->是否阻塞当前线程
- private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
- int ws = pred.waitStatus;
- //前一个节点的状态时SIGNAL,即释放锁后承诺唤醒当前节点,那么返回true可以阻塞当前线程
- if (ws == Node.SIGNAL)
- return true;
- //前一个节点状态>0,即CANCEL。
- //那么往前遍历找到没有取消的前置节点。同时从链表中移除CANCEL状态的节点
- if (ws > 0) {
- do {
- node.prev = pred = pred.prev;
- } while (pred.waitStatus > 0);
- pred.next = node;
- // 前置节点状态>=0,即0或者propagate。
- //这里通过CAS把前置节点状态改成signal成功获取锁,失败的话再阻塞。why?
- } else {
- compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
- }
- return false;
- }
复制代码 4.2.3. 解锁
- private final boolean parkAndCheckInterrupt() {
- //使用Unsafe阻塞当前线程,这里会清除线程中断的标记,因此需要返回中断的标记
- LockSupport.park(this);
- return Thread.interrupted();
- }
复制代码 4.2.3.1. 使用AQS释放锁
- public void unlock() {
- //简单得调用AQS的release方法
- sync.release(1);
- }
复制代码 4.2.3.2. 尝试释放锁
- public final boolean release(int arg) {
- //调用Sync释放锁成功
- if (tryRelease(arg)) {
- Node h = head;
- //队头不为空且状态正常,那么唤醒头节点
- if (h != null && h.waitStatus != 0)
- unparkSuccessor(h);
- return true;
- }
- return false;
- }
复制代码 4.2.3.2.1. 释放锁成功后唤醒阻塞队列中的后续节点
[code]private void unparkSuccessor(Node node) { int ws = node.waitStatus; //当前节点的状态0(即是取消状态) Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; //那么从队尾开始往前遍历找到离当前节点最近的下一个状态 |