JAVA并发编程(11)-JAVA重入锁的实现

重入锁
重入锁就是当线程A已经获取到了锁A,当再次需要获取锁A时,可以正常获取,不会被阻塞。就是一个线程可以多次获取同一个锁。synchronized 锁就是可重入的。java 1.5以后提供了ReentrantLock 实现重入锁功能。ReentrantLock 支持公平锁和非公平锁。

重进入的实现-非公平锁
前边两篇中我们提到过锁的底层实现就是 实现AQS的 tryAcquires 等方法。重入锁也一样。AQS调用tryAcquires 尝试获取锁,如果是公平的锁就调用公平的尝试获取锁的方法,否则就调用非公平的锁的方法。
非公平锁获取同步状态的方法如下:
JAVA并发编程(11)-JAVA重入锁的实现
可以看到除了正常获取锁的逻辑,增加了当前线程是否就是锁持有线程的判断。如果是就可以获取到锁。从而实现可重入。

JAVA并发编程(11)-JAVA重入锁的实现
锁的释放代码相对简单,解析如下:
JAVA并发编程(11)-JAVA重入锁的实现
重入锁的实现-公平锁
公平锁和非公平锁的实现类似,只不过增加了公平性的处理,那怎么处理的呢?前边我们知道AQS同步队列是FIFO的。所以只要实现如果队列中有排队的,申请获取锁的线程就必须进队列一起等待。这样就实现了锁的公平性。
公平锁尝试获取同步状态的代码如下:
JAVA并发编程(11)-JAVA重入锁的实现
代码中可以看到,获取锁时增加了判断队列中是否有等待的线程的判断,如果有那么 不允许竞争锁,如此便保证了公平性。

公平锁和费公平锁比较
公平锁可以解决线程的饥饿问题,就是等待时间最长的线程最新先获取到锁,但是效率相对低下。因为为了保证公平性,增加了线程的切换次数。而非公平锁虽然解决不了线程的饥饿问题,但是效率相对较高。
比如一个线程A获取到锁后 执行逻辑,这个时候 线程 B和线程C 来竞争锁,竞争失败进入同步队列。这个时候线程A 释放锁,释放后又立即竞争锁,公平和非公平锁逻辑如下:
按照公平的方式
由于线程B和线程C已经在同步队列中等待,那么线程A就必须也去同步队列中等待。按照时间顺序,B先获取锁,切换到线程B执行。然后依次切换到C,然后再切换到A 一共切换了 三次。
按照非公平的方式
线程A可以竞争锁,如果A竞争成功了,那么A继续执行。线程B和C继续等待。A执行完 然后 按照时间顺序 B获取锁并执行,然后C获取锁并执行。这样就线程状态就切换了两次。