0%

Java[并发]ReentrantLock实现分析

ReentrantLock重入锁,是指对于同一个线程,在再次获取锁时,不会阻塞,是一种优化锁。Synchronized关键字是支持隐式重入的同步,对于同一个线程,可以支持多次进入而不阻塞。
ReentrantLock支持可重入的实现主要是通过AQS中的volatile共享变量state的计数来实现的,对于同一个线程,只会进行计数,不会阻塞,相反,在释放同步状态时,需要释放所有计数,才会释放锁。 ReentrantLock支持公平锁和非公会锁,公平锁是指严格按照FIFO的规则,先阻塞的线程先获取锁,后阻塞的线程后获取锁。ReentrantLock通过内部实现的FairSycn和NonFairSync两个内部类的tryAcquire方法实现的不同来实现公平锁和非公会锁。
首先看ReentrantLock对AbstractQueuedSynchronizer的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
static abstract class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;

/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
* 由Lock接口的Lock.lock执行,主要原因是由子类来快速实现非公平锁版本,
*
*/
abstract void lock();

/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
//非公平锁的tryAcquire
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取同步计数
int c = getState();
if (c == 0) {
//如果计数为初始状态,马上设置state后返回,并把当前线程设置到独占者线程中
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前线程为前面的独占者的线程,将把计数+acquires后,设置回state变量
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;
}

//释放同步状态,公平和非公会锁都使用此方法释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//当前state为0时,说明释放成功,否则继续state变量-1
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
….

}

非公平锁版本的同步器实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
final static class NonfairSync extends Sync {
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//CAS同步状态变量成功即快速上锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}

protected final boolean tryAcquire(int acquires) {
//使用非公平锁版本的nonfairTryAcquire
return nonfairTryAcquire(acquires);
}

}

公平锁的同步器实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
final static class FairSync extends Sync {
final void lock() {
acquire(1);
}

/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//与非公平锁不同的地方,即加入同步队列的当前节点是否有前驱节点
//如果有前驱节点,必须等待前驱节点释放锁后才能获取锁
if (isFirst(current) &&
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;
}

}

可以通过ReentrantLock的构造方法来决定是使用公会锁还是非公平锁:

1
2
3
public ReentrantLock(boolean fair) {
sync = (fair)? new FairSync() : new NonfairSync();
}

如果使用没有参数的构造方法,使用的是非公平锁版本。

下面的例子测试公平锁和非公会锁的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class FairAndNonFairLockTest {


private static Lock fairLock = new ReentrantLock2(true);
private static Lock nonFairLock = new ReentrantLock2(false);

public static void testFairLock(){
for(int i = 0 ; i < 5; i++){
Job job = new Job(fairLock);
job.start();
}
}

public static void testNonFairLock(){
for(int i = 0 ; i < 5; i++){
Job job = new Job(nonFairLock);
job.start();
}
}

private static class Job extends Thread{
private Lock lock ;

public Job(Lock lock){
this.lock = lock;
}

@Override
public void run(){
while(true){
ReentrantLock2 reLock = (ReentrantLock2)lock;
reLock.lock();
try{
SleepUtils.second(1);
System.out.println("Locked by [" + reLock.getOwner().getName() + "];" +
"waiting by [" + StringUtils.join(reLock.getQueuedThreads(),",") + "]");
}finally{
reLock.unlock();
}
}
}
}

public static void main(String[] args) {
testFairLock();
//testNonFairLock();
}

private static class ReentrantLock2 extends ReentrantLock{

public ReentrantLock2(boolean fair){
super(fair);
}
//获取等待队列中的线程
public Collection<Thread> getQueuedThreads(){
List<Thread> threadList = new ArrayList<Thread>(super.getQueuedThreads());
Collections.reverse(threadList);
return threadList;
}

public Thread getOwner(){
return super.getOwner();
}

}

}

程序输出为:

1
2
3
4
5
6
Locked by [Thread-0];waiting by [Thread[Thread-2,5,main],Thread[Thread-1,5,main],Thread[Thread-3,5,main],Thread[Thread-4,5,main]]
Locked by [Thread-2];waiting by [Thread[Thread-1,5,main],Thread[Thread-3,5,main],Thread[Thread-4,5,main],Thread[Thread-0,5,main]]
Locked by [Thread-1];waiting by [Thread[Thread-3,5,main],Thread[Thread-4,5,main],Thread[Thread-0,5,main],Thread[Thread-2,5,main]]
Locked by [Thread-3];waiting by [Thread[Thread-4,5,main],Thread[Thread-0,5,main],Thread[Thread-2,5,main],Thread[Thread-1,5,main]]
Locked by [Thread-4];waiting by [Thread[Thread-0,5,main],Thread[Thread-2,5,main],Thread[Thread-1,5,main],Thread[Thread-3,5,main]]
Locked by [Thread-0];waiting by [Thread[Thread-2,5,main],Thread[Thread-1,5,main],Thread[Thread-3,5,main],Thread[Thread-4,5,main]]

可以看到,是按照等待队列中的线程顺序获取锁的
改为测试非公平锁的情况,输出为:

1
2
3
4
5
6
Locked by [Thread-3];waiting by [Thread[Thread-4,5,main],Thread[Thread-0,5,main],Thread[Thread-1,5,main],Thread[Thread-2,5,main]]
Locked by [Thread-4];waiting by [Thread[Thread-0,5,main],Thread[Thread-1,5,main],Thread[Thread-2,5,main],Thread[Thread-3,5,main]]
Locked by [Thread-0];waiting by [Thread[Thread-1,5,main],Thread[Thread-2,5,main],Thread[Thread-3,5,main],Thread[Thread-4,5,main]]
Locked by [Thread-1];waiting by [Thread[Thread-2,5,main],Thread[Thread-3,5,main],Thread[Thread-4,5,main],Thread[Thread-0,5,main]]
Locked by [Thread-1];waiting by [Thread[Thread-2,5,main],Thread[Thread-3,5,main],Thread[Thread-4,5,main],Thread[Thread-0,5,main]]
Locked by [Thread-1];waiting by [Thread[Thread-2,5,main],Thread[Thread-3,5,main],Thread[Thread-4,5,main],Thread[Thread-0,5,main]]

可以看到,有些线程可以重复获取锁而有些线程则处于“饥饿”的状态。
一般来说,非公平锁比公平锁性能更好,因为可以减少线程大量地切换。