实现方法
这里先解释一下公平和非公平的概念。
(1)公平,指的是竞争锁资源的线程,严格按照请求顺序来分配锁。
(2)非公平,表示竞争锁资源的线程,允许插队来抢占锁资源。
ReentnantLock 默认采用了非公平锁的策略来实现锁的竞争逻辑,如下图所示。 ReentrantLock内部使用了AQS来实现锁资源的竞争,没有竞争到锁资源的线程,会加入AQS同步队列,这个队列是一个FIFO的双向链表。
在这样的背景下,公平锁的实现方式就是,线程在竞争锁资源的时候判断AQS同步队列里有没有等待的线程。如果有,就加入队列的尾部等待。而非公平锁的实现方式就是,不管队列里有没有线程等待,它都会先尝试抢占锁资源,如果抢不到,则再加入 AQS同步队列进行等待。
ReentrantLock和synchronized 默认都是非公平的,之所以要这么设计,是因为考虑到了性能的问题。因为一个竞争锁的线程如果按照公平的策略去阻塞等待,同时 AOS再把等待队列里的线程唤醒,会涉及内核态的切换,对性能的影响比较大。如果使用非公平策略,当前线程正好在上一个线程释放锁的临界点抢到了锁,就意味着这个线程不需要切换到内核态,虽然对原本应该要被唤醒的线程不公平,但是提升了锁竞争的性能。
实现原理
ReentrantLock是一种可重入的排他锁,主要用来解决多线程对共享资源竞争的问题。它有3个比较核心的特性:
(1)它支持可重入,也就是获得锁的线程在释放锁之前再次去竞争同一把锁的时候,不需要加锁就可以直接访问。
(2)它支持公平和非公平特性。
(3)它提供了阻塞竞争锁和非阻塞竞争锁的两种方法,分别是lockO和tyLockO。
ReentrantLock 的底层实现有几种非常关键的技术。
第1种,锁的竞争,ReentrantLock是通过互斥变量,使用CAS机制来实现的,如下图所
示。
没有竞争到锁的线程,使用了AQS(AbstractQueuedSynchronizer)这样一个队列同步器来存储,底层是通过双向链表实现的。当锁被释放之后,会从AQS队列的头部唤醒下一个等待
第2种,公平和非公平的特性,主要体现在竞争锁的时候,需要判断AQS队列里是否锁的线程。
第3种,锁的重入特性,在 AOS 里一个成员变量保存当前获得锁的线程,当同一个线程下在等待中的线程。次再来竞争锁的时候,就不会按照锁竞争的逻辑处理,而是直接增加重入次数。