月泉的博客

JUC-ReentrantLock原理剖析

字数统计: 1.2k阅读时长: 5 min
2018/11/04 Share

阅读该篇文章希望您已经理解或者了解过AQS的设计,因为本篇并不会重复叙述AQS的内容,若需要可阅读我写过的AQS文章或者其它文章。

ReentrantLock是什么

ReentrantLock是一个显式独占可重入的公平或非公平锁,使用时可以设置锁是公平锁或非公平锁,它们之间的区别在于,线程唤醒的抢占方式,公平锁线程的抢占方式按照获取锁的先后顺序来唤醒,而非公平锁就是以一种随机抢占的方式。

ReentrantLock怎么用

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
public class ReentrantLockTest {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
new Thread(new Task("task1",lock)).start();
new Thread(new Task("task2",lock)).start();
new Thread(new Task("task3",lock)).start();
new Thread(new Task("task4",lock)).start();
new Thread(new Task("task5",lock)).start();
new Thread(new Task("task6",lock)).start();
new Thread(new Task("task7",lock)).start();

}

static final class Task implements Runnable{
private String name;
private Lock lock;

public Task(String name, Lock lock) {
this.name = name;
this.lock = lock;
}

@Override
public void run() {
lock.lock();
try{
System.out.println("The " + name + " start execute");
System.out.println("The " + name + " end executed");
}finally {
lock.unlock();
}
}
}
}

在默认情况下创建ReentrantLock实例创建的是一把非公平锁,如果要创建公平锁可使用有参的构造函数

1
new ReentrantLock(true)

ReentrantLock的实现原理

基本类结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ReentrantLock implements Lock, java.io.Serializable {
abstract static class Sync extends AbstractQueuedSynchronizer {
......
}

static final class NonfairSync extends Sync {
......
}

static final class FairSync extends Sync {
......
}

public ReentrantLock() {
......
}

public ReentrantLock(boolean fair) {
......
}
}

其实现了接口Lock并创建了3个内部类,一个抽象的Sync内部类和2个继承至这个抽象类的NonfairSyncFairSync分别代表非公平同步器和公平同步器

先从非公平的写起,首先看这个类的无参构造函数

1
2
3
public ReentrantLock() {
sync = new NonfairSync();
}

默认的无参构造函数会创建一个NofairSync的实例

然后我们在使用锁的时候通常会使用lockunlock方法,我们看其类的实现

1
2
3
public void lock() {
sync.acquire(1);
}
1
2
3
public void unlock() {
sync.release(1);
}

可以从源码上看到实际上就是调用的同步器中的acquirerelease的实例方法.

1
2
3
4
5
6
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}

NofairSync中覆盖了父类的tryAcquire方法,调用了父类的nonfairTryAcquire方法,看过我AQS那篇文章应该知道调用acquire方法时里面会调用tryAcquire方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread(); // 1
int c = getState(); // 2
if (c == 0) { // 3
if (compareAndSetState(0, acquires)) { // 4
setExclusiveOwnerThread(current); // 5
return true; // 6
}
}
else if (current == getExclusiveOwnerThread()) { // 7
int nextc = c + acquires; // 8
if (nextc < 0) // 9
throw new Error("Maximum lock count exceeded");
setState(nextc); // 10
return true;
}
return false;
}
  1. 获取当前线程
  2. 获取同步器中的state
  3. 判断状态是否为0(如果为0则锁未被其它线程占有)
  4. 更改线程占有标识
  5. 设置线程占有为当前线程
  6. 返回true获取成功
  7. 否则判断当前占有线程是否是现在要获取锁的线程(如果是代表线程重入锁)
  8. 获取得到当前重入后的重入数
  9. 小于0则达到最大重入数,抛出异常
  10. 否则设置状态

接着看释放锁的子类实现逻辑

1
2
3
4
5
6
7
8
9
10
11
12
protected final boolean tryRelease(int releases) {
int c = getState() - releases; // 1
if (Thread.currentThread() != getExclusiveOwnerThread()) // 2
throw new IllegalMonitorStateException();
boolean free = false; // 3
if (c == 0) { // 4
free = true; /// 5
setExclusiveOwnerThread(null); // 6
}
setState(c); // 7
return free;
}
  1. 当前状态减release
  2. 判断当前线程是否是当前独占持有线程,如果不是抛出异常IllegalMonitorStateException
  3. 是否锁成功标识
  4. 判断减后状态是否为0
  5. 释放成功标识为true
  6. 将当前独占持有线程置空
  7. 设置state(重入数量)
  8. 返回标识

可见非公平锁的实现机制非常简单,就是将AQS的state做为重入数

接着看下公平锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread(); // 1
int c = getState(); // 2
if (c == 0) { // 3
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) { // 4
setExclusiveOwnerThread(current); // 5
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 6
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}

可以看到公平锁重写了父类的tryAcquire方法

  1. 获取当前线程
  2. 获取AQS的state
  3. 判断当前锁是否被持有
  4. 调用父类AQS的hasQueuedPredecessors判断当前的线程是否前面还有排队的线程,如果没有就更改状态值
  5. 设置当前独占线程为当前线程
  6. 判断当前获取锁的线程是否是当前独占锁的线程,如果是就走重入锁步骤

原文作者:yuequan

原文链接:http://www.lunaspring.com/2018/11/04/juc_reentrant_lock/

发表日期:November 4th 2018, 7:04:00 pm

更新日期:June 20th 2019, 4:08:19 pm

版权声明:© 月泉 - 邓亮泉 版权所有

CATALOG
  1. 1. ReentrantLock是什么
  2. 2. ReentrantLock怎么用
  3. 3. ReentrantLock的实现原理