ReentrantLock。是一种可重入的排他锁,主要用来解决多线程对共享资源竞争的问题。
它支持可重入,在AQS的state状态值表示线程获取该锁的可重入次数,当同一个线程下次再来竞争锁的时候,就不会走锁竞争的逻辑,而是直接增加重入次数。也就是获得锁的线程在释放锁之前再次去竞争同一把锁的时候,不需要加锁就可以直接访问。
公平和非公平的特性,主要是体现在竞争锁的时候,是否需要判断AQS队列存在等待的线程。它的公平性与非公平性是根据构造器参数来决定其内部是一个公平还是非公平锁(默认),它提供了阻塞竞争锁和非阻塞竞争锁的两种方法,分别是lock()和tryLock()。
ReentrantLock的底层实现有几个非常关键的技术。锁的竞争,是通过互斥变量,使用CAS机制来实现的。没有竞争到锁的线程,使用AQS这样一个队列同步器来存储,底层是通过双向链表来实现的。当锁被释放之后,会从AQS队列里面的头部唤醒下一个等待锁的线程。
ReentrantLock的公平锁和非公平锁
默认采用了非公平锁的策略来实现锁的竞争逻辑。
其次,RL内部使用AQS来实现锁资源的竞争,没有竞争到锁资源的线程,会加入到AQS的同步队列里面,这个队列是一个FIFO的双向链表。
公平锁,指的是竞争锁资源的线程,严格按照请求顺序来分配锁。
公平锁的实现方式是线程在竞争锁资源的时候判断AQS同步队列里面有没有等待的线程,如果有就加入到队列的尾部等待。
非公平锁,表示竞争锁资源的线程,允许插队来抢占锁资源。
非公平锁的实现方式就是不管队列里面有没有线程等待,它都会先去尝试抢占资源,如果抢不到,再加入到AQS同步队列等待。
RL和synchronized默认都是非公平锁的策略,之所以要这么设计,我认为还是考虑到了性能这个方面的原因。
因为一个竞争锁的线程如果按照公平的策略去阻塞等待,同时AQS再把等待队列里面的线程唤醒,这里会涉及到内核态的切换,对性能影响比较大。
如果是非公平策略,当前线程正好在上一个线程释放锁的临界点抢占到了锁,就意味着这个线程不需要切换到内核态,虽然对原本应该唤醒的线程不公平,但是提升了锁竞争的性能。
ReentrantLock的公平锁和非公平的底层实现
1.第一处,在执行lock()方法时,
公平锁是不会直接用CAS修改state属性的,而是直接指向acquire()方法。
非公平锁无论怎样都会先用CAS尝试将state从0修改为1,那失败了才会执行acquire()方法。
2.第二处,在tryLock()方法时,
公平锁和非公平锁都要先判断state的属性是否为0,如果为0的话,公平锁要先查看AQS队列中是否有排队的线程,
如果没有排队的线程,那么直接用CAS的方式尝试获取锁资源,如果有排队的线程,就判断当前线程是否是排在队列的首位,
如果是,那么当前线程依然可以使用CAS尝试获取锁资源。如果AQS中有排队的线程,如果当前线程不是排在首位,那么就不会去竞争锁资源,而是直接去排队。
非公平锁,就是无论是否有线程在AQS队列中,都是直接执行CAS去修改state属性,尝试获取锁资源。