概念

ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java关键字synchronized隐式支持重入性, synchronized通过获取自增,释放自减的方式实现重入。与此同时,ReentrantLo

java中的可重入锁 java 可重入锁实现_加锁

ck还支持公平锁和非公平锁两种方式。那么,要想完完全全的弄懂ReentrantLock的话,主要也就是ReentrantLock同步语义的学习:1. 重入性的实现原理;2. 公平锁和非公平锁。

类图

重入性的实现原理

 

要想支持重入性,就要解决两个问题:

1. 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;

2. 由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功。

 

实现原理就是: 

加锁: 

1. 如果该锁未被任何线程占有,该锁能被当前线程获取.获取的话, 加锁 ( 计数器加1  )

2.若被占有,检查占有线程是否是当前线程 ,如果是的话, 继续加锁 ( 计数器加1  )

解锁:

1.只能由当前持有锁的线程释放锁

2.当锁的计数器为 0 的时候,释放锁

 

公平锁与公平锁

 

公平锁: 先入先出 FIFO

ReentrantLock 默认是非公平锁

构造方法: true时为公平锁 [  FairSync ] ,false时为非公平锁[ NonfairSync ]

 

  • 公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象
  • 公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。因此,ReentrantLock默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量

 

AQS 的 state 状 态值表示线程获取该锁的可重入次数 , 在默认情况下, state 的值为 0表示当前锁没有被任何线程持有 。 当一个线程第一次获取该锁时会尝试使用 CAS 设置 state 的值为 l,如果 CAS 成功则当前线程获取了该锁,然后记录该锁的持有者为当 前线程 。 在该线程没有释放锁的情况下第 二 次获取该锁后 ,状态值被设置为 2, 这就是可 重入次数 。 在该线程释放该锁时,会尝试使用 CAS 让状态值减 1, 如果减 l 后状态值为 0, 则 当前线程释放该锁 。

 

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

 

 

会查看当前锁的状态值是否为 0,为 0 则 说明当前该锁空闲,那么就 尝试 CAS 获取该锁,将 AQS 的状态值从 0 设置为 1,并设置当前锁的持有者为当前线程 然后返回, true。

如果当前状态值不为 0 则 说明该锁已经被某个线程持有,所以代码查看当前线程是否是该锁的持有者,如果当前线程是该锁的持有者,则状态值加 1,然后 返回 true, 这里需要注意, nextc<0说明可重入次数溢出了。

如果当前线程不是锁的持有 者则返回 false,然后其会被放入 AQS 阻塞队列。