【线程】ReentrantLock 内部公平锁与非公平锁实现 (十)

我的原则:先会用再说,内部慢慢来


一、概念

  1. 公平锁: 遵循FIFO,先来先服务的原则。
  2. 非公平锁: 哄抢锁,谁先抢到服务谁。

二、ReentrantLock 架构

在这里插入图片描述

三、FailSync 与 NonfairSync 核心代码区别

  1. 根据 AQS 实现类的不同,FairSync + NonfairSync 的tryAcquire实现不同
  2. Sync#nonfairTryAcquire 非公平模式获取锁方法
  3. FairSync#tryAcquire 公平模式获取锁方法
  1. AbstractQueuedSynchronizer#acquire 方法
public final void AbstractQueuedSynchronizer#acquire(int arg) {
	/* 
	 * 根据 AQS 实现类的不同,FairSync + NonfairSync 的tryAcquire实现不同
	 * 1. NonfaireSync 在 tryAcquire 很大机率获得锁 ,不往下走了
	 * 2。 FairSync 在 tryAcquire ,若遇到Sync队列有Node,那么就直接获取失败,进入 acquireQueued 去排队。
	 */
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  1. Sync#nonfairTryAcquire 非公平模式获取锁方法
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
    	// 公平锁与非公平锁区别就在这,非公平锁模式下,一会机会直接就去争夺锁了。无需FIFO
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    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;
}
  1. FairSync#tryAcquire 公平模式获取锁方法
protected final boolean FairSync#tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
    	// 公平锁与非公平锁区别就在这,公平锁模式下,抢夺锁之前,会去判断同步队列里面是否有线程等待,有的话,就去排队
        if (!hasQueuedPredecessors() &&
            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;
}
  1. 解释一下代码,在队列有一排Node的情况下,为什么 c == 0 ?

A1 拿到锁还没处理完,A2 - AN 阻塞当中,Sync 队列里面包含 A2 - AN,此时,A1刚好处理完,currentThread = null,B1 刚好来 lock.lock ,这时候 c 就是 0,那么接下去怎么处理。

  1. NonfaireSync模式下 直接抢,不管你们那些排队的
  2. FairSync 模式下,退出去排队。

四、代码 demo

  1. FairSync 模式
public class _12_02_TestCondition {
	public static void main(String[] args) throws Exception{
		LockDemo lockDemo = new LockDemo();
		for (int i = 0; i < 5; i++) {
			new Thread(() -> {
				try {
					lockDemo.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			},"A" + i).start();
		}
		// 确保上面代码先执行
		Thread.sleep(1000);
		Scanner sc = new Scanner(System.in);
		System.out.println("点击任意键唤醒线程 ...");
		sc.nextLine();

		for (int i = 0; i < 5; i++) {
			new Thread(() -> {
				lockDemo.signal();

			},"B" + i).start();
		}
	}
	static class LockDemo{
		ReentrantLock lock = new ReentrantLock(true);
		Condition condition = lock.newCondition();
		public void await() throws Exception{
			System.out.println(Thread.currentThread().getName() + ",condition ready to lock ===");
			lock.lock();
			System.out.println(Thread.currentThread().getName() + ",condition ready to await ");
			condition.await();
			System.out.println(Thread.currentThread().getName() + ",condition ready to unlock ===");
			lock.unlock();
		}
		public void signal(){
			System.out.println(Thread.currentThread().getName() + ",condition ready to lock ");
			lock.lock();
			System.out.println(Thread.currentThread().getName() + ",condition ready to signal ");
			condition.signal();
			System.out.println(Thread.currentThread().getName() + ",condition ready to unlock ");
			lock.unlock();
		}
	}
}
    1. FairSync 模式下输出:
A0,condition ready to lock ===
A1,condition ready to lock ===
A0,condition ready to await 
A2,condition ready to lock ===
A3,condition ready to lock ===
A4,condition ready to lock ===
A1,condition ready to await 
A2,condition ready to await 
A3,condition ready to await 
A4,condition ready to await 
点击任意键唤醒线程 ...


B0,condition ready to lock 
B0,condition ready to signal 
B0,condition ready to unlock 
B1,condition ready to lock 
A0,condition ready to unlock ===
B2,condition ready to lock 
B1,condition ready to signal 
B1,condition ready to unlock 
B3,condition ready to lock 
B2,condition ready to signal 
B2,condition ready to unlock 
A1,condition ready to unlock ===
B4,condition ready to lock 
B3,condition ready to signal 
B3,condition ready to unlock 
A2,condition ready to unlock ===
B4,condition ready to signal 
B4,condition ready to unlock 
A3,condition ready to unlock ===
A4,condition ready to unlock ===
  1. 改成 NonfairSync 模式

ReentrantLock lock = new ReentrantLock(); // 默认非公平锁

  1. NonfairSync 模式下输出
A0,condition ready to lock ===
A1,condition ready to lock ===
A2,condition ready to lock ===
A0,condition ready to await 
A3,condition ready to lock ===
A3,condition ready to await 
A1,condition ready to await 
A2,condition ready to await 
A4,condition ready to lock ===
A4,condition ready to await 
点击任意键唤醒线程 ...


B0,condition ready to lock 
B1,condition ready to lock 
B0,condition ready to signal 
B0,condition ready to unlock 
B2,condition ready to lock 
B2,condition ready to signal 
B2,condition ready to unlock 
B3,condition ready to lock 
B1,condition ready to signal 
B4,condition ready to lock 
B1,condition ready to unlock 
A0,condition ready to unlock ===
A3,condition ready to unlock ===
B3,condition ready to signal 
B3,condition ready to unlock 
A1,condition ready to unlock ===
B4,condition ready to signal 
B4,condition ready to unlock 
A2,condition ready to unlock ===
A4,condition ready to unlock ===

结论:

ReentrantLock lock = new ReentrantLock(true); 公平锁模式下,谁先 lock ,谁就先 unlock。
ReentrantLock lock = new ReentrantLock(); 公平锁模式下,不一定。

四、番外篇

下一章节:【线程】ReentrantReadWriteLock 内部共享锁与排他锁源码分析 (十一)
上一章节:【线程】ReentrantLock + Condition 源码剖析 (九)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章