有两种队列,都是非阻塞式队列
- 同步队列(FIFO队列)
- 条件队列
都共用了节点ADT
非阻塞队列与阻塞队列比较
- 队列遵循先进先出,后进后出的原则。
- 阻塞式队列比非阻塞式队列性好。
区别:
阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列
线程的阻塞与解除阻塞
利用LockSupport.park() 和 LockSupport.unpark() 实现线程的阻塞和唤醒(底层调用Unsafe的native park和unpark实现),同时支持超时时间。
AQS 里将阻塞线程封装到一个内部类 Node 里。并维护一个 CLH Node FIFO 队列。 CLH队列是一个非阻塞的 FIFO 队列,往里面插入或移除一个节点的时候,在并发条件下不会阻塞,而是通过自旋锁和 CAS 保证节点插入和移除的原子性。
参数
//表示共享
static final Node SHARED = new Node();
//表示独占
static final Node EXCLUSIVE = null;
//当前节点线程被取消
static final int CANCELLED = 1;
//当前节点的后继节点的线程在等待锁
static final int SIGNAL = -1;
//当前节点时条件队列的节点
static final int CONDITION = -2;
//表示应该无条件传播,失败主动取消会设置这个类型
static final int PROPAGATE = -3;
//标识状态,取上面几个int类型的值
volatile int waitStatus;
//当前节点的前驱
volatile Node prev;
//当前节点的后继
volatile Node next;
//当前节点属于的线程
volatile Thread thread;
//如果是条件队列,就指向下一个节点,否则被赋值成shared的static final Node SHARED = new Node();
Node nextWaiter;
独占模式下acquire函数
- 修改当前锁的状态state
- 设置持有锁的线程
共享模式下acquire函数
- 共享锁可以被多个线程同时拿到,具体同步器的实现类,是否允许,看每个同步器自己的实现。
- 节点的waitStatus设置为SHARED
- 只修改state字段,不设置持有锁的线程
acquire操作
- 入队
- 申请入队
独占模式下release函数
操作,释放锁
- 检查当前线程是否和持有锁的线程是同一个线程
- 修改锁的状态,state字段
- 唤醒队到中头节点的下一个节点的线程
唤醒的动作:
- 将自己的waitStatus设成0
- 真正的叫起线程
头结点的下一个节点
- 检查自己的前驱节点是不是头节点
- 争用锁
共享模式下release函数
- 修改锁状态,修改state字段
- 唤醒队列中头节点的下一个节点的线程
- 取当前被唤醒的节点的下一个节点,如果下一个节点也是SHARED模式,则直接唤醒
条件队列
await操作
1,创建条件节点,必要的时候初始化条件队列或者把自己加入到条件队列中
2,释放当前线程占有的锁,执行AQS release操作
3,挂起当前线程
4,被唤醒以后,执行AQS acquire入队列的第二步操作:申请入队列
signal操作
1,条件队列中的节点(firstWaiter)转移到同步队列中
2,把刚刚加入到同步队列中的条件节点的前驱的waitStatue =-1(当前节点的后继节点的线程在等待锁)
独占模式代表ReentrantLock
nonfairTryAcquire函数
final boolean nonfairTryAcquire(int acquires) {
//current:获取当前线程
final Thread current = Thread.currentThread();
int c = getState();
//无锁状态
if (c == 0) {
//设置state,设置持有锁的线程,返回true
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//判断当前线程和持有锁的线程是否是同一个线程,不是直接返回false,争取资源失败
else if (current == getExclusiveOwnerThread()) {
//这里就是可重入的逻辑,lock几次就加几次
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
addWaiter函数
//返回当前创建的节点
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//未初始化队列,先初始化队列,enq函数返回的是前驱节点
enq(node);
return node;
}
acquireQueued函数
final boolean acquireQueued(final Node node, int arg) {
//标记是否成功拿到资源
boolean failed = true;
try {
//标记等待过程中是否被中断过
boolean interrupted = false;
//自旋
for (;;) {
//p:当前节点的前驱结点
final Node p = node.predecessor();
//如果前驱是头结点会有一次额外机会争用锁(比如刚把队列构造好,还未真正入队列)
if (p == head && tryAcquire(arg)) {
//当前节点设置为头节点
setHead(node);
//为了回收
p.next = null; // help GC
failed = false;
return interrupted;
}
//第一个条件:true:当前节点等待被唤醒,若为false,表示当前节点刚挂上来,又从上边开始执行,获得一次争取锁的机会
//第二个条件:挂起当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire函数
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//如果为-1,表示后继节点等待被唤醒,直接返回true
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//在默认为0的情况下,强制设置前驱节点状态为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
release函数
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
//第一个条件:同步队列存在
//第二个条件,要唤醒后边的节点,自己的状态为-1
if (h != null && h.waitStatus != 0)
//传入头结点,唤醒下一个节点
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease
protected final boolean tryRelease(int releases) {
//当前状态-1
int c = getState() - releases;
//当前线程是否和持有锁的线程是同一个线程
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
//清除锁的要释放的线程信息
setExclusiveOwnerThread(null);
}
//0
setState(c);
//状态为0,完全被释放(重入锁的可能性)
return free;
}
unparkSuccessor函数
唤醒等待队列中下一个线程
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//true:需要唤醒下一个节点,且改自己状态为0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
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;
}
//唤醒操作,回到在acquireQueued被挂起操作执行死循环里边的东西去尝试获取锁,并不是一定获取锁,可能新入队列的也会去争取锁,这就是非公平的体现
if (s != null)
LockSupport.unpark(s.thread);
}
公平的tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//第一个条件:false:当前节点前边还有节点,不能争取锁,保证了公平性
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;
}