Java并发编程--AQS
概述
抽象队列同步器(AbstractQueuedSynchronizer,简称AQS)是用来构建锁或者其他同步组件的基础框架,它使用一个整型的volatile变量(命名为state)来维护同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
volatile变量的读写和CAS是concurrent包得以实现的基础。CAS表示如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值,此操作具有volatile读和写的内存语义。AQS通过volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。
高层类 | Lock 同步器 阻塞队列 Executor 并发容器 |
基础类 | AQS 非阻塞数据结构 原子变量类 |
volatile变量的读/写 CAS |
concurrent包的实现结构如上图所示,AQS、非阻塞数据结构和原子变量类等基础类都是基于volatile变量的读/写和CAS实现,而像Lock、同步器、阻塞队列、Executor和并发容器等高层类又是基于基础类实现。
AQS的域和方法
域
private transient volatile Node head; //同步队列的head节点
private transient volatile Node tail; //同步队列的tail节点
private volatile int state; //同步状态
方法
AQS提供的可以修改同步状态的3个方法:
protected final int getState(); //获取同步状态
protected final void setState(int newState); //设置同步状态
protected final boolean compareAndSetState(int expect, int update); //CAS设置同步状态
AQS提供的模板方法,主要有以下三类:
1)独占式获取和释放同步状态
public final void acquire(int arg) //独占式获取同步状态,如果不成功会进入同步队列等待。
public final void acquireInterruptibly(int arg) //与acquire不同的是,能响应中断
public final boolean tryAcquireNanos(int arg, long nanosTimeout) //增加超时机制 public final boolean release(int arg) //独占式释放同步状态,该方法会调用重写的tryRelease(int arg)。
以上三种获取同步状态的方法都会调用自定义的tryAcquire(int arg)方法,acquire的获取失败时的入队等待机制、acquireInterruptibly的响应中断机制、tryAcquireNanos的超时机制等AQS已经实现好,这样开发人员只需要实现自己的获取同步状态机制,就可以大大降低了实现一个可靠自定义同步组件的门槛。
2)共享式获取和释放同步状态
public final void acquireShared(int arg) //共享式获取同步状态,如果不成功会进入同步队列等待。与独占式不同的是,同一时刻可以有多个线程获取到同步状态。
public final void acquireSharedInterruptibly(int arg) //可响应中断
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) //超时机制 public final boolean releaseShared(int arg) //共享式释放同步状态,该方法会调用重写的tryReleaseShared(int arg)。
同样以上三种获取同步状态的方法会调用自定义的tryAcquireShared方法。
3)查询同步队列中的等待线程情
publicfinalCollection<Thread>getQueuedThreads()
publicfinalbooleanhasQueuedThreads()//返回包含可能正在等待获取的线程列表,需要遍历链表。返回的只是个估计值,且是无序的。这个方法的主要是为子类提供的监视同步队列措施而设计。
。。。
AQS提供的自定义方法
以上AQS的方法都为final方法,不能被子类重写,因为它们对于任何自定义同步器应该是不需要更改的,下面为AQS提供的可以重写的方法。开发者需要根据自定义同步组件的特点,重写以下方法。这些方法的实现在内部必须是线程安全的,通常应该很短并且不被阻塞。
protected boolean tryAcquire(int arg) //独占式获取同步状态,此方法应该查询是否允许它在独占模式下获取对象状态,如果允许,则获取它。返回值语义:true代表获取成功,false代表获取失败。
protected boolean tryRelease(int arg) //独占式释放同步状态 protected int tryAcquireShared(int arg) //共享式获取同步状态,返回值语义:负数代表获取失败、0代表获取成功但没有剩余资源、正数代表获取成功,还有剩余资源。
protected boolean tryReleaseShared(int arg) //共享式释放同步状态 protected boolean isHeldExclusively() //AQS是否被当前线程所独占
AQS的使用
怎么使用AQS实现自定义同步组件?
自定义同步组件实例一:独占锁(使用独占式的获取与释放)。
Mutex同步组件同一时刻只允许一个线程占用锁,不支持可重入。0表示未锁定状态,1表示锁定状态。
class Mutex implements Lock, java.io.Serializable { //静态内部类,自定义同步器
private static class Sync extends AbstractQueuedSynchronizer { // 释放处于占用状态(重写isHeldExclusively)Report whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
} // 独占式获取锁(重写tryAcquire) Acquire the lock if state is zero
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) { //CAS设置状态为1。
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
} // 独占式释放锁(重写tryRelease) Release the lock by setting state to zero
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) //获取状态
throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0); //设置状态为0
return true;
} // Provide a Condition
//每个Condition都包含一个队列
Condition newCondition() { return new ConditionObject(); } // Deserialize properly
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
} // The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync(); //仅需要将操作代理到sync
public void lock() { sync.acquire(1); } //调用AQS的模板方法,
public boolean tryLock() { return sync.tryAcquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public boolean isLocked() { return sync.isHeldExclusively(); }
public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
自定义同步组件实例二:锁存器(使用共享的获取与释放方法)
BooleanLatch可用在多个线程需要等待某个事件发生才能继续执行的情况中。初始状态state=0, 此时所有线程获取同步状态方法tryAcquireShared返回-1,即获取失败,入等待队列。直到有线程调用tryReleaseShared释放同步状态,被阻塞的状态才会进行执行。
class BooleanLatch { private static class Sync extends AbstractQueuedSynchronizer {
boolean isSignalled() { return getState() != 0; } protected int tryAcquireShared(int ignore) {
return isSignalled()? 1 : -1;
} protected boolean tryReleaseShared(int ignore) {
setState(1);
return true;
}
} private final Sync sync = new Sync();
public boolean isSignalled() { return sync.isSignalled(); }
public void signal() { sync.releaseShared(1); }
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
}
AQS的实现原理
同步队列
同步队列中的节点Node用来保存获取同步状态失败的线程引用、等待状态、以及前驱和后继节点。
AQS中两个域:head节点和tail节点,组成一个FIFO的双向队列。
private transient volatile Node head;
private transient volatile Node tail;
Node源码如下。
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node(); //共享方式
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null; //独占方式 /** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1; //waitStatus=1为取消状态
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1; //后继节点的线程处于等待状态,需要被唤醒。
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2; //当前线程在condition上等待
static final int PROPAGATE = -3; //表示下一次共享式同步状态获取将会无条件的被传播下去。 volatile int waitStatus; //等待状态,0-初始状态
volatile Node prev; //前驱节点
volatile Node next; //后继节点
volatile Thread thread; //获取同步的线程
Node nextWaiter; final boolean isShared() {
return nextWaiter == SHARED;
}
//返回前驱节点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
} Node() { // Used to establish initial head or SHARED marker
} Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
} Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
独占式获取同步状态
1)调用自定义的tryAcquire方法,该方法要保证线程线程安全的获取同步状态(如Mutex中的tryAcquire使用CAS更新保证原子性),如果获取成功则return。
2)如果获取失败,构造Node,并通过addWaiter方法将节点插入队列尾部。Node.EXCLUSIVE表示节点以独占方式等待。
3)acquireQueued方法中该节点自旋方式尝试获取同步状态。如果获取不到同步状态,则阻塞节点中的线程,被阻塞线程唤醒依靠前驱节点的出队或阻塞线程被终端来实现。
//该方法对中断不敏感,也就是由于线程获取同步状态失败后进入同步队列中,后续对线程进行中断操作时,线程不会从同步队列中移出.
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
} //将节点添加到队尾
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//快速尝试入队,如果失败则需要调用enq(node)方法入队,这样做有什么好处?有一定概率减少一次方法调用
//compareAndSetTail保证Node入队是线程安全的
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
} //初始化或自旋CAS直到入队成功
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
入同步队列之后怎么获取同步状态?阻塞机制是怎样的?
//自旋方式尝试获取同步状态
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); //获取当前节点的前驱节点
if (p == head && tryAcquire(arg)) { //如果前驱节点是head节点则尝试获取同步状态
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) //判断当前线程是否应该被阻塞,如果是则阻塞直到被唤醒继续循环,如果不是则再次尝试获取同步状态。
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
} //判断当前线程是否应该被阻塞,如果线程应该被阻塞则返回true。检查和更新获取同步状态失败Node的前驱节点的waitStatus。
//其中pred为node的前驱节点
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.
*/
//前驱节点已经设置为SIGNAL状态,在前驱节点的线程释放同步状态会唤醒当前Node的线程。
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
//前驱节点是cancelled状态,跳过被取消的Node,直到向前找到waitStatus > 0的Node作为当前节点的前驱,然后重试获取同步状态。
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/* waitStatus = 0是初始状态。
* 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.
*/
//将前驱节点的等待状态改为SIGNAL。
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
} //阻塞线程
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); //使用LockSupport阻塞当前线程
return Thread.interrupted();
}
在获取同步状态时,同步器维护一个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列(或停止自旋)的条件是前驱节点为头节点且成功获取了同步状态。
独占式释放同步状态
在释放同步状态时,同步器调用tryRelease(int arg)方法释放同步状态,然后唤醒头节点的后继节点。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); //唤醒后继节点线程
return true;
}
return false;
} //唤醒后继节点
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.
*/
//将当前节点的waitStatus改为0-原始状态,目的是什么?
int ws = node.waitStatus;
if (ws < 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.
*/
//如果后继节点为null或被取消,则从tail向前找到正常的后继节点
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)
LockSupport.unpark(s.thread); //唤醒后继节点
}
共享式获取同步状态
1)首先调用自定义方法tryAcquireShared尝试获取同步状态,至少调用一次tryAcquireShared方法,如果返回值>=0,则获取成功,return;否则执行步骤2),
2)当获取失败时,为当前线程以共享方式创建Node并插入同步队列。
3)入队后,以自旋方式尝试获取同步状态,如果前驱节点为head节点,则尝试获取同步状态,获取失败,则阻塞线程。
//共享式获取同步状态,忽略异常。
//注意:实现自定义方法tryAcquireShared时,要遵循AQS定义的返回值语义,负数代表获取失败、0代表获取成功但没有剩余资源、正数代表获取成功,还有剩余资源。
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) //
doAcquireShared(arg);
} private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED); //为当前线程以共享方式创建Node并插入同步队列尾部。
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) { //如果前驱节点为head节点,则尝试获取同步状态
int r = tryAcquireShared(arg);
if (r >= 0) { //获取成功
setHeadAndPropagate(node, r); //设置node为head节点,还有剩余资源则继续唤醒后继的node
p.next = null; // help GC
if (interrupted) //如果等待过程中被中断过,则中断当前线程
selfInterrupt(); //Thread.currentThread().interrupt();
failed = false;
return;
}
}
//shouldParkAfterFailedAcquire方法判断当前线程是否应该被阻塞,如果是则调用parkAndCheckInterrupt阻塞当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
} //获取成功后,设置node为head节点,
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node); //设置node为head节点,因此node出队
//如果还有剩余资源,尝试唤醒node节点的后继节点
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
} //设置node为head节点,因此node出队
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
共享式释放同步状态
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
} //唤醒后继节点线程并确保被传播
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h); //唤醒后继节点
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
独占式可中断获取
可响应中断获取与普通获取的区别:当线程被中断时,会立即返回,并抛出InterruptedException,执行cancelAcquire方法取消获取同步状态;而普通获取只是将中断标志位置为true,但线程依旧会阻塞在等待队列中。
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException(); //抛出中断异常
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
} private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException(); //唯一的区别,抛出中断异常。而普通的获取操作,只是将中断标志位置为true。
}
} finally {
if (failed)
cancelAcquire(node);
}
}
如何取消线程?
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return; node.thread = null; //将线程置为null // Skip cancelled predecessors
Node pred = node.prev;
while (pred.waitStatus > 0) //即waitStatus=1,为cancelled状态。跳过状态为取消的前驱节点
node.prev = pred = pred.prev; // predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
Node predNext = pred.next; // Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
node.waitStatus = Node.CANCELLED; //当前节点置为cancelled状态 // If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) { //如果当前节点为tail节点,删除该节点
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
//
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
//如果前驱节点waitStatus为SIGNAL或者CAS更新为SIGNAL成功,则pred释放同步状态时会通知后继节点
//并且pred.thread不为null,则cas将pred的后继节点置为node.next,
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node); //唤醒后继节点
} node.next = node; // help GC next指向自己,node从等待队列中移除。
}
}
独占式可超时获取
在响应中断的基础上增加了超时机制
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout);
} private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
long lastTime = System.nanoTime();
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
if (nanosTimeout <= 0) //判断是否超时,nanosTimeout<=0表示超时,如果超时则return false.
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout); //如果还剩余的时间nanosTimeout>阈值(spinForTimeoutThreshold=1000纳秒),则阻塞当前线程nanosTimeout纳秒。当nanosTimeout<1000纳秒时,则不阻塞当前线程,而是进入快速的自旋过程。原因在于,非常短的超时等待无法做到十分精确,如果这时再进行超时等待,相反会让nanosTimeout的超时从整体上表现得反而不精确。因此,在超时非常短的场景下,同步器会进入无条件的快速自旋。
long now = System.nanoTime(); //当前时间
//lastTime表示上次唤醒时间,now - lastTime表示已经睡眠的时间
nanosTimeout -= now - lastTime; //nanosTimeout表示还剩余的时间,nanosTimeout>0表示表示超时时间未到
lastTime = now;
if (Thread.interrupted())
throw new InterruptedException(); //抛异常
}
} finally {
if (failed)
cancelAcquire(node);
}
}
总结,三种方式获取同步状态方式的对比,主要区别在于获取同步状态失败时的处理逻辑:
acquire方法直接阻塞线程,不响应中断,只是将中断标记置为true,但线程依旧会阻塞在等待队列中。
acquireInterruptibly方法直接阻塞线程,响应中断,当线程被中断时,会立即返回,并抛出InterruptedException。
tryAcquireNanos方法将线程阻塞nanosTimeout秒,如何超时还未获取到同步状态,则返回。同时支持响应中断。
1 public final void acquire(int arg) //独占式获取同步状态,如果不成功会进入同步队列等待。
2 public final void acquireInterruptibly(int arg) //与acquire不同的是,能响应中断
3 public final boolean tryAcquireNanos(int arg, long nanosTimeout) //增加超时机制
LockSupport
在阻塞和唤醒线程时,使用了LockSupport类。LockSupport提供了一系列阻塞和唤醒线程的公共方法。底层使用unsafe提供的方法实现。
void park() //阻塞当前线程,只有调用unpark或中断才能从park方法中返回
void parkNanos(long nanos) //超时阻塞当前线程,超时则返回
void parkUntil(long deadline) //截止时间阻塞当前线程,直到deadline void unpark(Thread thread) //唤醒处于阻塞状态的线程 //jdk1.6中增加了以下方法,blocker表示当前线程要等待的对象。线程dump信息比使用park方法要多,方便问题排查和监控。
void park(Object blocker)
void parkNanos(Object blocker, long nanos)
void parkUntil(Object blocker, long deadline)
参考资料:
《Java并发编程的艺术》
《Java并发之AQS详解》http://www.cnblogs.com/waterystone/p/4920797.html
Java并发编程--AQS的更多相关文章
- JAVA并发-同步器AQS
什么是AQS aqs全称为AbstractQueuedSynchronizer,它提供了一个FIFO队列,可以看成是一个用来实现同步锁以及其他涉及到同步功能的核心组件,常见的有:ReentrantLo ...
- 【Java并发编程实战】----- AQS(四):CLH同步队列
在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...
- 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport
在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...
- 【Java并发编程实战】----- AQS(二):获取锁、释放锁
上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...
- Java并发编程总结3——AQS、ReentrantLock、ReentrantReadWriteLock(转)
本文内容主要总结自<Java并发编程的艺术>第5章——Java中的锁. 一.AQS AbstractQueuedSynchronizer(简称AQS),队列同步器,是用来构建锁或者其他同步 ...
- java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock
原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...
- Java并发编程-看懂AQS的前世今生
在具备了volatile.CAS和模板方法设计模式的知识之后,我们可以来深入学习下AbstractQueuedSynchronizer(AQS),本文主要想从AQS的产生背景.设计和结构.源代码实现及 ...
- Java并发编程总结3——AQS、ReentrantLock、ReentrantReadWriteLock
本文内容主要总结自<Java并发编程的艺术>第5章——Java中的锁. 一.AQS AbstractQueuedSynchronizer(简称AQS),队列同步器,是用来构建锁或者其他同步 ...
- Java并发编程:用AQS写一把可重入锁
Java并发编程:自己动手写一把可重入锁详述了如何用synchronized同步的方式来实现一把可重入锁,今天我们来效仿ReentrantLock类用AQS来改写一下这把锁.要想使用AQS为我们服务, ...
随机推荐
- 初试PHP连接sql server
最开始想使用 pdo_sqlsrv 拓展,但是一直没成功,本文采用的是 pdo_dblib + freetds. 环境:CentOS 6.8.PHP 5.6.20 freetds wget ftp:/ ...
- linux下,把屏幕竖起来
xrandr -o left 向左旋转90度 xrandr -o right 向右旋转90度 xrandr -o inverted 上下翻转 xrandr -o normal 回到正常角度
- Git ---游离状态下的commit 分支切换与找回,commit之后无法找到历史记录
commit之后无法找到历史记录 https://blog.csdn.net/zyb2017/article/details/78307688
- Hive 压缩技术Data Compression
Mapreducwe 执行流程 :input > map > shuffle > reduce > output 压缩执行时间,map 之后,压缩,数据存储在本地磁盘,减少磁盘 ...
- 变量存储类型(auto static extern)
auto 动态存储类型变量(函数内部变量存储默认为 auto型) auto只用于函数内部定义,单片机在执行这个函数时为它分配内存地址,当函数执行完毕返回后,auto变量会被销毁,再次进入这个函数时,它 ...
- Android Studio卡在refreshing gradle project的原因和快速解决办法
Android Studio更新后一直Refreshing的解决办法! 这个问题遇到过很多次,网上也有很多解决办法,但是好像都没有发现refreshing gradle project在做什么. 一般 ...
- java面试二
技术交流群: 233513714 126.什么是ORM?答:对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题 ...
- 2.栅格的类中同时设置col-md-* col-sm-*的作用
1.一般设定成这样的话,在小屏幕上会堆叠在一起 <div class="row"> <div class="col-md-4 ">COL ...
- 三层还是DDD,ORM还是Ado.Net,何去何从?
我本想把这个问题放到博问去,前几次有去博问问过之类的问题,无奈大神们可能都不屑回答别人的低级问题.所以放到随笔里,一方面把自己对ORM.架构的一些看法写下来抛砖引玉,另一方面最主要的是想寻求大神们指指 ...
- python学习笔记五:模块和包
一.模块用import导入 cal.py: #!/usr/bin/python def add(x,y): return x+y if __name__ == '__main__': print ad ...