欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【并发编程】ReentrantLock

程序员文章站 2022-05-04 20:53:18
...

ReentrantLock是什么

    ReentrantLock继承自AbstractQueuedSynchronizer,即AQS, AQS可以理解为一个线程排队获取CPU的队列。队列中放的是一个个的Node。

  *      +------+  prev +-----+       +-----+
  * head |      | <---- |     | <---- |     |  tail
  *      +------+       +-----+       +-----+
 	volatile Node prev;
 	volatile Node next;
 	volatile Thread thread;

获取锁的过程

通过以下代码模拟多线程竞争锁的场景

public class Example1 {
	public static void main(String[] args) {
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,5,2000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
		ReentrantLock reentrantLock = new ReentrantLock(true);
		for (int i = 0; i < 3; i++) {
			threadPoolExecutor.submit(() -> {
				reentrantLock.lock();
				logic();
				reentrantLock.unlock();
			});
		}
		threadPoolExecutor.shutdown();
	}

	public static void logic() {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

head == tail == null

【并发编程】ReentrantLock

  • 第一个线程调用tryAcquire(arg)过来获取锁,此时state为0,说明锁是空闲状态,可以继续执行获取流程。
  • 接下来尝试获取锁,首先通过hasQueuedPredecessors()判断队列有没有线程在排队,第一个线程过来时队列时空的,head==tail ==null,即没有线程排队。
  • 直接尝试CAS获取锁
    【并发编程】ReentrantLock
  • 如果获取成功,记录锁被当前线程持有,setExclusiveOwnerThread(current)。

head == tail != null

  • 第二个线程调用tryAcquire(arg)过来获取锁,此时state为1,不会继续执行获取锁的流程,但是会判断是否重入锁。
  • 如果getExclusiveOwnerThread()是当前线程,则该线程可以得到锁。
  • 否则进入队列
    【并发编程】ReentrantLock
    入队列之前会将当前线程封装成一个Node,Node的前一个节点prev为第一个线程对应的Node,Node的下一个节点next为null,即该Node为tail节点。
    【并发编程】ReentrantLock
  • 入队列之后继续尝试获取锁
    【并发编程】ReentrantLock
	// 当该节点的上一个节点是head节点时才尝试再次获取锁
	if (p == head && tryAcquire(arg)) {
		setHead(node);
	    p.next = null; // help GC
	    failed = false;
	    return interrupted;
	}
  • 第二个线程走到这里是满足条件的,可以获取到锁。
    【并发编程】ReentrantLock

head != tail

  • 第三个线程调用tryAcquire(arg)过来获取锁,此时state为1,不会继续执行获取锁的流程。
  • 进入队列继续尝试获取锁。
  • 第三个线程的前一个Node不是head,所以不能紧接着尝试获取锁。
    【并发编程】ReentrantLock
  • 判断是否需要park,第三个线程此时不需要park。(暂未模拟到需要park的场景)
  • 尝试获取锁。
  • 获取锁成功。