ReentrantLock使用介绍

深入学习Java基础知识
2021-05-17 14:29 · 阅读时长12分钟
小课

ReentrantLock是一个可重入的互斥锁,也就是说该锁在某一时刻只能被同一个线程所持有一次或者多次,通过调整构造参数可以实现公平锁和非公平锁。

一、ReentrantLock的公平锁和非公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

ReentrantLock有一个带参的构造方法,通过传入true/false,可以实现公平锁和非公平锁,两者在获取锁时的策略有所不同。

  • 公平锁,在获取锁时,如果当前锁没有被其它线程持有,先判断等待队列中是否有线程在等待,如果有则加入到队列末尾排队,没有则直接获取锁。
  • 非公平锁,在获取锁时,如果当前锁没有被其它线程持有,无论等待队列中是否有线程等待,先尝试获取锁,获取失败再加入到队列末尾。

当调用ReentrantLock的lock方法时,会根据公平或非公平调用具体同步器的tryAcquire方法,公平锁同步器FairSync.tryAcquire实现如下:

1protected final boolean tryAcquire(int acquires) {
2    final Thread current = Thread.currentThread();
3    int c = getState();
4    if (== 0) {
5        //当前锁没有被其它线程持有,先判断队列中是否有线程在等待,没有的话才尝试获取锁
6        if (!hasQueuedPredecessors() &&
7            compareAndSetState(0, acquires)) {
8            setExclusiveOwnerThread(current);
9            return true;
10        }
11    }
12    else if (current == getExclusiveOwnerThread()) {
13        int nextc = c + acquires;
14        if (nextc < 0)
15            throw new Error("Maximum lock count exceeded");
16        setState(nextc);
17        return true;
18    }
19    return false;
20}

FairSync.tryAcquire源码参考:ReentrantLock.java

注释

非公平锁同步器NonfairSync.tryAcquire实现如下:

1protected final boolean tryAcquire(int acquires) {
2    return nonfairTryAcquire(acquires);
3}
4
5final boolean nonfairTryAcquire(int acquires) {
6    final Thread current = Thread.currentThread();
7    int c = getState();
8    if (== 0) {
9        //当前锁没有被其它线程持有,不判断队列中是否有线程在等待,直接尝试获取锁
10        if (compareAndSetState(0, acquires)) {
11            setExclusiveOwnerThread(current);
12            return true;
13        }
14    }
15    else if (current == getExclusiveOwnerThread()) {
16        int nextc = c + acquires;
17        if (nextc < 0) // overflow
18            throw new Error("Maximum lock count exceeded");
19        setState(nextc);
20        return true;
21    }
22    return false;
23}

NonfairSync.tryAcquire源码参考:ReentrantLock.java

注释
二、ReentrantLock使用示例

1、通过使用ReentrantLock,修改main方法实现多线程顺序打印。

1public static void main(String[] args) throws Exception {
2    for (int i = 0; i < 10; i++) {
3        int num = i;
4        new Thread(() -> {
5            printNumber(num);
6        }).start();
7    }
8}
9
10private static void printNumber(int num) {
11    System.out.println(num);
12}

2、通过使用ReentrantLock,修改完善下面代码实现生产者消费者模型。

1import java.util.ArrayDeque;
2import java.util.Queue;
3
4public class Main {
5
6    private static final Queue<Object> queue = new ArrayDeque<>();
7    private static final int MAX_SIZE = 5;
8
9    public static void main(String[] args) {
10        for (int i = 0; i < 5; i++) {
11            new Thread(new Producer()).start();
12        }
13        for (int i = 0; i < 5; i++) {
14            new Thread(new Consumer()).start();
15        }
16    }
17
18    /**
19     * 生产者
20     */
21    static class Producer implements Runnable {
22
23        @Override
24        public void run() {
25       
26        }
27    }
28
29    /**
30     * 消费者
31     */
32    static class Consumer implements Runnable {
33
34        @Override
35        public void run() {
36
37        }
38    }
39}
ReentrantLockjava线程锁