*前置知识:
1,线程的生命周期:新建(new())--就绪(start())--运行(run())--阻塞--死亡;
线程的非运行状态: 等待,挂起,睡眠,阻塞
正常生命周期中运行的线程,如果有同步需求,假如正在竞争一把锁,在没拿到锁时,应该被挂起。(而不是什么都不做)
2,在队列中的线程,应该处于阻塞状态,并在必要的时候被唤醒。线程的挂起和唤醒会引发线程用户态和内核态的切换。
一个线程管理框架的要点:
1,通用,底层通用,与上层业务解耦;
2,利用CAS,原子修改共享标记位;
3,等待队列(阻碍其它线程);
AQS(线程同步队列)基本实现原理:
state标记位
Node的结构:{
线程对象
等待状态(4个枚举值:CANCELED,SIGNAL,CONDITION,PROPAGATE)
前、后指针
}
入队
1,尝试获取锁(修改标记位),立即返回;tryAcquire
2,获取锁(修改标记位),愿意进入等待队列,直到获取。acquire
tryAcquire --> 上层业务逻辑;不想等待锁,自己处理;
protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
选择等待锁,调用acquire方法
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
addWaiter方法(),将当前线程封装成队列节点,模式为exclusive,即独占;插入到队尾;
1 /** 2 * Creates and enqueues node for current thread and given mode. 3 * 4 * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared 5 * @return the new node 6 */ 7 private Node addWaiter(Node mode) { 8 Node node = new Node(Thread.currentThread(), mode); 9 // Try the fast path of enq; backup to full enq on failure 10 Node pred = tail; 11 if (pred != null) { 12 node.prev = pred; 13 if (compareAndSetTail(pred, node)) { 14 pred.next = node; 15 return node; 16 } 17 } 18 enq(node); 19 return node; 20 }View Code
acquireQueued()方法,对队列中的线程进行挂起
LockSupport.park(this);
挂起当前线程的条件:
AQS的双向队列头节点只是一个占位,并不是真正的线程节点。
curr.pre == head 判断当前节点的前置节点是不是头节点,是的话当前节点才能拿锁(当前节点位于队头)
*始终保证head之后只有一个节点在通过CAS在拿锁,其它线程对象都应该被挂起。
设计要点:
始终保证队列里只有一个线程在自旋获取锁,其它线程都是已经被挂起或者正在被挂起;
出队
出队则唤醒线程:
tryRelease()
release()
唤醒线程,LockSupport.unpark(thread);
同时,将全局volatile变量State的值设置为0
上面是同步队列,还有一个条件队列。
*AQS哪些地方在自旋?
可以通过看ReentrantLock源码辅助看AQS源码。
标签:node,Node,AQS,队列,线程,原理,节点 From: https://www.cnblogs.com/hangwei/p/16936893.html