案例:一共A,B,C三个线程去银行办理业务,但是银行的窗口只有一个,即如果银行窗口有人在办理业务,其他的线程必须在候客区等待。代码演示如下:这里假设线程A先获取窗口,并在窗口办理业务的时间比较长
package com.atguigu.juc.aqs;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* @auther zzyy
* @create 2021-03-27 15:46
*/
public class AQSDemo
{
public static void main(String[] args)
{
ReentrantLock reentrantLock = new ReentrantLock();//非公平锁
// A B C三个顾客,去银行办理业务,A先到,此时窗口空无一人,他优先获得办理窗口的机会,办理业务。
// A 耗时严重,估计长期占有窗口
new Thread(() -> {
reentrantLock.lock();
try
{
System.out.println("----come in A");
//暂停50分钟线程
try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); }
}finally {
reentrantLock.unlock();
}
},"A").start();
//B是第2个,B一看到受理窗口被A占用,只能去候客区等待,进入AQS队列,等待着A办理完成,尝试去抢占受理窗口。
new Thread(() -> {
reentrantLock.lock();
try
{
System.out.println("----come in B");
}finally {
reentrantLock.unlock();
}
},"B").start();
//C是第3个,C一看到受理窗口被A占用,只能去候客区等待,进入AQS队列,等待着A办理完成,尝试去抢占受理窗口,前面是B顾客,FIFO
new Thread(() -> {
reentrantLock.lock();
try
{
System.out.println("----come in C");
}finally {
reentrantLock.unlock();
}
},"C").start();
}
}
对于线程A来说
此时银行处于的状态如下图所示
对于线程B来说,线程A已经占据着窗口,此时线程B进来
此时银行的场景如下图所示
/**
* pred为哨兵结点,node为线程B的Node结点
* 此时两个结点中的waitStatus属性值都是默认值为0
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;//ws=0
if (ws == Node.SIGNAL)//Node.SIGNAL=-1
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
//最终执行改行代码,将哨兵节点的waitStatus设置为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
//第一次执行返回false
return false;
}
/**
* pred为哨兵结点,node为线程B的Node结点
* 此时哨兵结点中的waitStatus属性值为-1
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
//直接返回true
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
此时银行的场景如下图所示
接着线程C过来了,执行流程跟线程B差不多,最终银行的场景如下图所示:
接着线程A办理完业务了,调用unlock方法了。
/**
* node为哨兵结点,哨兵结点的waitStatus=-1
*/
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
//将哨兵结点的waitStatus设置为0
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
//哨兵结点的下一个结点为线程B的Node结点
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//唤醒线程B
LockSupport.unpark(s.thread);
}
此时唤醒了线程B,回到线程B暂停的代码如下:
此时银行得场景如下图所示
后续流程类似…