概述

  抽象队列同步器(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的更多相关文章

  1. JAVA并发-同步器AQS

    什么是AQS aqs全称为AbstractQueuedSynchronizer,它提供了一个FIFO队列,可以看成是一个用来实现同步锁以及其他涉及到同步功能的核心组件,常见的有:ReentrantLo ...

  2. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  3. 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport

    在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...

  4. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  5. Java并发编程总结3——AQS、ReentrantLock、ReentrantReadWriteLock(转)

    本文内容主要总结自<Java并发编程的艺术>第5章——Java中的锁. 一.AQS AbstractQueuedSynchronizer(简称AQS),队列同步器,是用来构建锁或者其他同步 ...

  6. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  7. Java并发编程-看懂AQS的前世今生

    在具备了volatile.CAS和模板方法设计模式的知识之后,我们可以来深入学习下AbstractQueuedSynchronizer(AQS),本文主要想从AQS的产生背景.设计和结构.源代码实现及 ...

  8. Java并发编程总结3——AQS、ReentrantLock、ReentrantReadWriteLock

    本文内容主要总结自<Java并发编程的艺术>第5章——Java中的锁. 一.AQS AbstractQueuedSynchronizer(简称AQS),队列同步器,是用来构建锁或者其他同步 ...

  9. Java并发编程:用AQS写一把可重入锁

    Java并发编程:自己动手写一把可重入锁详述了如何用synchronized同步的方式来实现一把可重入锁,今天我们来效仿ReentrantLock类用AQS来改写一下这把锁.要想使用AQS为我们服务, ...

随机推荐

  1. Ado访问sqlserver 端口号非1433时 连接串的写法

    Provider=SQLOLEDB.;Persist Security Info=False;Data Source=hostName,Port //注意用 逗号分隔主机名与端口号

  2. hadoop的shuffle过程

    1. shuffle: 洗牌.发牌——(核心机制:数据分区,排序,缓存): shuffle具体来说:就是将maptask输出的处理结果数据,分发给reducetask,并在分发的过程中,对数据按key ...

  3. pytorch中如何使用预训练词向量

    不涉及具体代码,只是记录一下自己的疑惑. 我们知道对于在pytorch中,我们通过构建一个词向量矩阵对象.这个时候对象矩阵是随机初始化的,然后我们的输入是单词的数值表达,也就是一些索引.那么我们会根据 ...

  4. Altium Designer使用5:AD18的DXP在什么地方?

    1.在顶上的菜单栏右击

  5. Linux YUM (Yellowdog Updater, Modified) Commands for Package Management

    Linux YUM (Yellowdog Updater, Modified) Commands for Package Management In this article, we will lea ...

  6. hadoop中节点上的nodemanager一直启动不起来

    当我们启动Hadoop集群的时候,发现有一台机器的nodemanager启动后自动关闭, 查看日志的时候发现有错误:yarn-root-nodemanager-log 解决办法: netstat  a ...

  7. Pycharm的使用一

    一.编辑器的选择 Python 的学习过程少不了集成开发环境(IDE)或者代码编辑器,这些 Python 开发工具帮助开发者加快使用 Python 开发的速度,提高效率. 高效的代码编辑器或者 IDE ...

  8. C#方法参数

    使用静态字段来模拟全局变量. 如果调用者想要得到被调用者的值: 1.返回值 2.不管是实参还是形参,都是在内存中开辟了空间的. 3.方法的功能一定要单一. GetMax(int n1,int n2) ...

  9. Erlang OTP学习:supervisor [转]

    转自: http://diaocow.iteye.com/blog/1762895 今天细致的看了下supervisor,现在做个总结: 其中,方块代表supervisor process,它的功能很 ...

  10. KVO的底层实现原理?如何取消系统默认的KVO并手动触发?

    KVO是基于runtime机制实现的 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类(该类的子类),在这个派生类中重写基类中任何被观察属性的setter 方法.派生类在被 ...