synchronousqueue场景_SynchronousQueue解析上-TransferStack

http://donald-draper.iteye.com/blog/2363491

AbstractQueue简介:http://donald-draper.iteye.com/blog/2363608

ConcurrentLinkedQueue解析:http://donald-draper.iteye.com/blog/2363874

BlockingQueue接口的定义:http://donald-draper.iteye.com/blog/2363942

LinkedBlockingQueue解析:http://donald-draper.iteye.com/blog/2364007

ArrayBlockingQueue解析:http://donald-draper.iteye.com/blog/2364034

PriorityBlockingQueue解析:http://donald-draper.iteye.com/blog/2364100

SynchronousQueue是同步的队列,里面涉及到数据结构和一些算法的知识,今天我们虚心来看一下,能得到多少,是多少。欢迎网友,给出不同的建议,进行共同学习交流。

package java.util.concurrent;

import java.util.concurrent.locks.*;

import java.util.concurrent.atomic.*;

import java.util.*;

/**

* A {@linkplain BlockingQueue blocking queue} in which each insert

* operation must wait for a corresponding remove operation by another

* thread, and vice versa. A synchronous queue does not have any

* internal capacity, not even a capacity of one. You cannot

* peek at a synchronous queue because an element is only

* present when you try to remove it; you cannot insert an element

* (using any method) unless another thread is trying to remove it;

* you cannot iterate as there is nothing to iterate. The

* [i]head[/i] of the queue is the element that the first queued

* inserting thread is trying to add to the queue; if there is no such

* queued thread then no element is available for removal and

* poll() will return null. For purposes of other

* Collection methods (for example contains), a

* SynchronousQueue acts as an empty collection. This queue

* does not permit null elements.

*

SynchronousQueue阻塞队列,每次插入操作必须等待一个协同的移除线程,反之亦然。

SynchronousQueue同步队列没有容量,可以说,没有一个容量。由于队列中只有在消费线程,

尝试消费元素的时候,才会出现元素,所以不能进行peek操作;不能用任何方法,

生产元素,除非有消费者在尝试消费元素,同时由于队列中没有元素,所以不能迭代。

head是第一个生产线程尝试生产的元素;如果没有这样的生产线程,那么没有元素可利用,

remove和poll操作将会返回null。SynchronousQueue实际一个空集合类。同时同步队列不允许为null。

*

Synchronous queues are similar to rendezvous channels used in

* CSP and Ada. They are well suited for handoff designs, in which an

* object running in one thread must sync up with an object running

* in another thread in order to hand it some information, event, or

* task.

*

同步队列与CSP和Ada场景下的通道相似(具体CSP和Ada可以google,我查的意思

为CSP-Constraint Satisfaction Problem,只有这个意思看上去有点像,怎么感觉不对,

据说CSP在机器学习中很有用,Ada查的靠谱一点的意思为美国军方的程序设计语言,其他的

都不靠谱,看到这篇文章的网友,可以看一下,可以给我发私信或留言,探讨一下)。

同步队列适用于传输通道设计,一个线程同步或生产一个元素,消息,资源,同时

另一个线程消费这些资源或任务。

*

This class supports an optional fairness policy for ordering

* waiting producer and consumer threads. By default, this ordering

* is not guaranteed. However, a queue constructed with fairness set

* to true grants threads access in FIFO order.

*

同步队列支持生产者和消费者等待的公平性策略。默认情况下,不能保证生产消费的顺序。

如果一个同步队列构造为公平性,则可以线程以FIFO访问队列元素。

*

This class and its iterator implement all of the

* [i]optional[/i] methods of the {@link Collection} and {@link

* Iterator} interfaces.

*

实现了所有Collection和Iterator接口

*

This class is a member of the

*

* Java Collections Framework.

*

* @since 1.5

* @author Doug Lea and Bill Scherer and Michael Scott

* @param the type of elements held in this collection

*/

public class SynchronousQueue extends AbstractQueue

implements BlockingQueue, java.io.Serializable {

private static final long serialVersionUID = -3223113410248163686L;

/*

* This class implements extensions of the dual stack and dual

* queue algorithms described in "Nonblocking Concurrent Objects

* with Condition Synchronization", by W. N. Scherer III and

* M. L. Scott. 18th Annual Conf. on Distributed Computing,

* Oct. 2004 (see also

* http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/duals.html).

同步队列实现拓展了双栈和双队列算法(条件同步的非阻塞并发对象),

在分布计算年刊中有具体描述,见下面连接

* The (Lifo) stack is used for non-fair mode, and the (Fifo)

* queue for fair mode. The performance of the two is generally

* similar. Fifo usually supports higher throughput under

* contention but Lifo maintains higher thread locality in common

* applications.

*

LIFO栈用于非公平模式,FIFO队列用于公平模式。两者的性能大体相同。

FIFO通常用于有高吞吐量存在竞争的场景,LIFO栈用于

Lifo maintains higher thread locality in common

applications.这句不翻译了,保持原味。

* A dual queue (and similarly stack) is one that at any given

* time either holds "data" -- items provided by put operations,

* or "requests" -- slots representing take operations, or is

* empty. A call to "fulfill" (i.e., a call requesting an item

* from a queue holding data or vice versa) dequeues a

* complementary node. The most interesting feature of these

* queues is that any operation can figure out which mode the

* queue is in, and act accordingly without needing locks.

*

双队列是一个在任何时候持有由put操作提供元素的data,slots表示的

take操作的请求,或为空队列,与栈相似。一个调用fulfill操作(请求队列中

的持有元素,即进行put操作),将会有一个不足元素出队列,反之亦然,

意思为一个take操作对一个put操作,一个put操作必须对应一个take操作。

这种队列最有趣的特点是,任何操作不根据锁,可以判断进队列的模式,

是非公平的LIFO栈stack还是公平的FIFO队列queue。

* Both the queue and stack extend abstract class Transferer

* defining the single method transfer that does a put or a

* take. These are unified into a single method because in dual

* data structures, the put and take operations are symmetrical,

* so nearly all code can be combined. The resulting transfer

* methods are on the long side, but are easier to follow than

* they would be if broken up into nearly-duplicated parts.

*

队列和栈继承了Transferer类,Transferer定义简单的方法(转换,转让)

做put或take操作。因为在双数据结构中,put和take操作是对称的,所以他们

统一定义在一个方法中,所以几乎所有的代码可以放在一起。

The resulting transfer

methods are on the long side, but are easier to follow than

they would be if broken up into nearly-duplicated parts.

这段不翻译保持原味。

* The queue and stack data structures share many conceptual

* similarities but very few concrete details. For simplicity,

* they are kept distinct so that they can later evolve

* separately.

*

队列和栈数据结构有许多概念上相同的属性,但也有一些具体的不同。

为了简单起见,他们保持着区别,确保later evolve separately。

* The algorithms here differ from the versions in the above paper

* in extending them for use in synchronous queues, as well as

* dealing with cancellation. The main differences include:

*

这个算法与上面论文中的算法有所不同,我们扩展为了论文中的算法用在同步

队列中,也用于处理cancellation。主要的不同包括:

* 1. The original algorithms used bit-marked pointers, but

* the ones here use mode bits in nodes, leading to a number

* of further adaptations.

* 2. SynchronousQueues must block threads waiting to become

* fulfilled.

* 3. Support for cancellation via timeout and interrupts,

* including cleaning out cancelled nodes/threads

* from lists to avoid garbage retention and memory depletion.

*

1.原始算法中用了bit标记指针,本同步队列实现算法中,在节点中使用bits模式,

将导致number进一步的调整。

2.同步队列必须阻塞线程等待变的可填充。

3.支持通过中断和超时取消等待策略,包括从等待队列中清除取消的节点或线程,

以避免产生垃圾,和内存泄漏。

* Blocking is mainly accomplished using LockSupport park/unpark,

* except that nodes that appear to be the next ones to become

* fulfilled first spin a bit (on multiprocessors only). On very

* busy synchronous queues, spinning can dramatically improve

* throughput. And on less busy ones, the amount of spinning is

* small enough not to be noticeable.

*

通过LockSupport的park/unpark方法,实现阻塞,除了在多处理器上,

下一个变得可填充的先自旋的节点或线程。在繁忙的同步队列中,自旋可以显著

提高吞吐量。在不繁忙时,自旋并不太多的消耗。

* Cleaning is done in different ways in queues vs stacks. For

* queues, we can almost always remove a node immediately in O(1)

* time (modulo retries for consistency checks) when it is

* cancelled. But if it may be pinned as the current tail, it must

* wait until some subsequent cancellation. For stacks, we need a

* potentially O(n) traversal to be sure that we can remove the

* node, but this can run concurrently with other threads

* accessing the stack.

*

在队列和栈中,清除操作有着不同的实现。在队列中,当一个节点或线程取消时,

我们大多数情况下,可以立即以常量1(一致性检查尝试次数的模)的时间移除一个节点或线程。

但是如果一直在队列的尾部,则必须等后来的线程节点取消。对于栈,

我们可能需要时间O(n)遍历已确定那个节点我们可以移除,但是这个可以与

其他线程并发访问栈。

* While garbage collection takes care of most node reclamation

* issues that otherwise complicate nonblocking algorithms, care

* is taken to "forget" references to data, other nodes, and

* threads that might be held on to long-term by blocked

* threads. In cases where setting to null would otherwise

* conflict with main algorithms, this is done by changing a

* node's link to now point to the node itself. This doesn't arise

* much for Stack nodes (because blocked threads do not hang on to

* old head pointers), but references in Queue nodes must be

* aggressively forgotten to avoid reachability of everything any

* node has ever referred to since arrival.

然而垃圾回收器必须关注其他复杂非阻塞算法的节点再生问题,数据,节点的引用

及线程也在会通过阻塞其他线程,以便长期持有锁。以防此类情况的发生,

引用将会为设置为null,以免与主要算法冲突,本算法姐姐方法是节点链接指向其自己。

这样不为引起大量的栈节点(因为阻塞线程,不能停留在head指针上),但是为了

避免其他的所有节点与以前引用的节点可达,队列节点的引用必须显示忘记索引。

*/

/**

* Shared internal API for dual stacks and queues.

双栈和队列共享内部API,队列和栈的父类

*/

abstract static class Transferer {

/**

* Performs a put or take.

*

执行一个put或take操作

* @param e if non-null, the item to be handed to a consumer;

* if null, requests that transfer return an item

* offered by producer.

如果元素为非空,则交给消费者处理,如果为null,请求生产者

生产一个元素,并返回元素

* @param timed if this operation should timeout 是否超时

* @param nanos the timeout, in nanoseconds 超时时间

* @return if non-null, the item provided or received; if null,

* the operation failed due to timeout or interrupt --

* the caller can distinguish which of these occurred

* by checking Thread.interrupted.

返回元素,如果非null,要不是队列中已经存在的,要不是生产者刚生产的。

如果为null,以为着由于超时,中断导致操作失败,调用可以通过检查线程中断位,

辨别放生了哪一种情况。

*/

abstract Object transfer(Object e, boolean timed, long nanos);

}

/** The number of CPUs, for spin control 获取运行时环境的处理个数*/

static final int NCPUS = Runtime.getRuntime().availableProcessors();

/**

* The number of times to spin before blocking in timed waits.

* The value is empirically derived -- it works well across a

* variety of processors and OSes. Empirically, the best value

* seems not to vary with number of CPUs (beyond 2) so is just

* a constant.

在超时等待阻塞前,自旋尝试的次数,这个值是一个,在不同处理器和系统性能上

良好工作的经验值。经验上来讲,最好的值,不要随着CPUS的个数/2的值变动,

所以它是一个常量,当处理器个数小于2,则为0,否则为32。

*/

static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;

/**

* The number of times to spin before blocking in untimed waits.

* This is greater than timed value because untimed waits spin

* faster since they don't need to check times on each spin.

在非超时等待阻塞之前,自旋的次数,最大非超时自旋时间大于最大自旋

时间,因为由于非超时自旋不需要在每次自旋时,不需要检查时间,所以,

非超时自旋非常快。

*/

static final int maxUntimedSpins = maxTimedSpins * 16;

/**

* The number of nanoseconds for which it is faster to spin

* rather than to use timed park. A rough estimate suffices.

快速自旋的时间,而不是park的时间,一个粗略的估计值。

*/

static final long spinForTimeoutThreshold = 1000L;

}

下面来看dual队列和栈的实现

先看栈:

/** Dual stack */

static final class TransferStack extends Transferer {

/*

* This extends Scherer-Scott dual stack algorithm, differing,

* among other ways, by using "covering" nodes rather than

* bit-marked pointers: Fulfilling operations push on marker

* nodes (with FULFILLING bit set in mode) to reserve a spot

* to match a waiting node.

本stack实现的是算法是拓展了Scherer-Scott双栈的算法,所不同的时,用

covering节点,而不是bit-marked指针:在bit集填充模式下,填充操作将会为

匹配一个等待节点保留资源,生产一个标记节点。

*/

/* Modes for SNodes, ORed together in node fields */

/** Node represents an unfulfilled consumer REQUEST节点表示一个未填充的消费者*/

static final int REQUEST = 0;

/** Node represents an unfulfilled producer DATA节点表示一个未填充的生产者*/

static final int DATA = 1;

/** Node is fulfilling another unfulfilled DATA or REQUEST

FULFILLING节点表示生产者正在给等待资源的消费者补给资源,或生产者在等待消费者消费资源/

static final int FULFILLING = 2;

/** Return true if m has fulfilling bit set

如果m是一个填充为单元,则返回true*/

static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }

/** Node class for TransferStacks. 栈节点 */

static final class SNode {

volatile SNode next; // next node in stack 节点的后继

volatile SNode match; // the node matched to this 匹配节点

volatile Thread waiter; // to control park/unpark 等待者线程

Object item; // data; or null for REQUESTs 数据,消费者消费的资源

int mode;//节点模式

// Note: item and mode fields don't need to be volatile

// since they are always written before, and read after,

// other volatile/atomic operations.

//元素item和mode需要要可见,由于他们总是在其他可见/原子操作写之前,读之后

SNode(Object item) {

this.item = item;

}

//设置节点后继

boolean casNext(SNode cmp, SNode val) {

return cmp == next &&

UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);

}

/**

* Tries to match node s to this node, if so, waking up thread.

* Fulfillers call tryMatch to identify their waiters.

* Waiters block until they have been matched.

*

尝试匹配目标节点与本节点,如果匹配,可以唤醒线程。补给者调用tryMatch方法

确定它们的等待线程。等待线程阻塞到它们自己被匹配。如果匹配返回true。

* @param s the node to match

* @return true if successfully matched to s

*/

boolean tryMatch(SNode s) {

if (match == null &&

UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {

Thread w = waiter;

//如果等待者不为null,则unpark等待线程

if (w != null) { // waiters need at most one unpark

waiter = null;

LockSupport.unpark(w);

}

return true;

}

return match == s;

}

/**

* Tries to cancel a wait by matching node to itself.节点尝试取消等待

*/

void tryCancel() {

UNSAFE.compareAndSwapObject(this, matchOffset, null, this);

}

//match指向自己,则取消等待

boolean isCancelled() {

return match == this;

}

// Unsafe mechanics

private static final sun.misc.Unsafe UNSAFE;

private static final long matchOffset;

private static final long nextOffset;

static {

try {

UNSAFE = sun.misc.Unsafe.getUnsafe();

Class k = SNode.class;

matchOffset = UNSAFE.objectFieldOffset

(k.getDeclaredField("match"));

nextOffset = UNSAFE.objectFieldOffset

(k.getDeclaredField("next"));

} catch (Exception e) {

throw new Error(e);

}

}

}

/** The head (top) of the stack 栈头节点*/

volatile SNode head;

//CAS操作nh为当前head,并比较head旧值是否为h

boolean casHead(SNode h, SNode nh) {

return h == head &&

UNSAFE.compareAndSwapObject(this, headOffset, h, nh);

}

/**

* Creates or resets fields of a node. Called only from transfer

* where the node to push on stack is lazily created and

* reused when possible to help reduce intervals between reads

* and CASes of head and to avoid surges of garbage when CASes

* to push nodes fail due to contention.

创建或重新设置节点的fields。在节点入栈懒创建,在当可能需要保证减少intervals(间隔)

读和head的CAS操或避免由于竞争CAS操作节点入栈引起的垃圾时,此方法会被transfer调用

*/

static SNode snode(SNode s, Object e, SNode next, int mode) {

if (s == null) s = new SNode(e);

s.mode = mode;

s.next = next;

return s;

}

/**

* Puts or takes an item.

put或take一个元素

*/

Object transfer(Object e, boolean timed, long nanos) {

/*

* Basic algorithm is to loop trying one of three actions:

*

算法的基本步骤是,循环尝试一下3步

* 1. If apparently empty or already containing nodes of same

* mode, try to push node on stack and wait for a match,

* returning it, or null if cancelled.

*

1.如果队列为空或已经包含相同模式的节点,则尝试节点入栈,等待匹配,

返回,如果取消返回null。

* 2. If apparently containing node of complementary mode,

* try to push a fulfilling node on to stack, match

* with corresponding waiting node, pop both from

* stack, and return matched item. The matching or

* unlinking might not actually be necessary because of

* other threads performing action 3:

*

2.如果包含一个互补模式的节点(take(REQUEST)->put(DATA);put(DATA)->take(REQUEST)),

则尝试一个FULFILLING节点入栈,同时匹配等待的协同节点,两个节点同时出栈,返回匹配的元素。

由于其他线程执行步骤3,实际匹配和解除链接指针动作不会发生。

* 3. If top of stack already holds another fulfilling node,

* help it out by doing its match and/or pop

* operations, and then continue. The code for helping

* is essentially the same as for fulfilling, except

* that it doesn't return the item.

3.如果栈顶存在另外一个FULFILLING的节点,则匹配节点,并出栈。这段的代码

与fulfilling相同,除非没有元素返回

*/

SNode s = null; // constructed/reused as needed

//根据元素判断节点模式,元素不为null,则为DATA,否则为REQUEST

int mode = (e == null) ? REQUEST : DATA;

for (;;) {

SNode h = head;

if (h == null || h.mode == mode) { // empty or same-mode

//如果是空队列,或栈头节点的模式与要放入的节点模式相同

if (timed && nanos <= 0) { // can't wait

//如果超时,则取消等待,出栈,设置栈头为其后继

if (h != null && h.isCancelled())

casHead(h, h.next); // pop cancelled node

else

//否则返回null

return null;

} else if (casHead(h, s = snode(s, e, h, mode))) {

//如果非超时,则将创建的新节点入栈成功,即放在栈头,自旋等待匹配节点(timed决定超时,不超时)

SNode m = awaitFulfill(s, timed, nanos);

if (m == s) { // wait was cancelled

//如果返回的是自己,节点取消等待,从栈中移除,并遍历栈移除取消等待的节点

clean(s);

return null;

}

if ((h = head) != null && h.next == s)

//s节点匹配成功,则设置栈头为s的后继

casHead(h, s.next); // help s's fulfiller

//匹配成功,REQUEST模式返回,匹配到的节点元素(DATA),DATA模式返回当前节点元素

return (mode == REQUEST) ? m.item : s.item;

}

} else if (!isFulfilling(h.mode)) { // try to fulfill

//如果栈头节点模式不为Fulfilling,判断是否取消等待,是则出栈

if (h.isCancelled()) // already cancelled

casHead(h, h.next); // pop and retry

//非取消等待,则是节点入栈

else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {

for (;;) { // loop until matched or waiters disappear

SNode m = s.next; // m is s's match

//后继节点为null,则出栈

if (m == null) { // all waiters are gone

casHead(s, null); // pop fulfill node

s = null; // use new node next time

break; // restart main loop

}

SNode mn = m.next;

//尝试匹配是s节点

if (m.tryMatch(s)) {

//匹配成功两个节点则出栈,

casHead(s, mn); // pop both s and m

return (mode == REQUEST) ? m.item : s.item;

} else // lost match

//否则,跳过s的后继节点

s.casNext(m, mn); // help unlink

}

}

} else { // help a fulfiller

//如果栈头节点模式为Fulfilling,找出栈头的匹配节点

SNode m = h.next; // m is h's match

if (m == null) // waiter is gone

//如果无后继等待节点,则栈头出栈

casHead(h, null); // pop fulfilling node

else {

//尝试匹配,如果匹配成功,栈头和匹配节点出栈,否则跳过后继节点

SNode mn = m.next;

if (m.tryMatch(h)) // help match

casHead(h, mn); // pop both h and m

else // lost match

h.casNext(m, mn); // help unlink

}

}

}

}

/**

* Spins/blocks until node s is matched by a fulfill operation.

自旋或阻塞,直到节点被一个fulfill操作匹配

*

* @param s the waiting node 等待被匹配的节点

* @param timed true if timed wait 是否超时等待

* @param nanos timeout value 时间值

* @return matched node, or s if cancelled 如果匹配返回节点,否则取消等待

*/

SNode awaitFulfill(SNode s, boolean timed, long nanos) {

/*

* When a node/thread is about to block, it sets its waiter

* field and then rechecks state at least one more time

* before actually parking, thus covering race vs

* fulfiller noticing that waiter is non-null so should be

* woken.

*

当一个节点线程将要阻塞时,在实际park之前,设置等待线程的field,重新至少检查

自身状态一次,这样可以避免在fulfiller注意到有等待线程非null,可以操作时,掩盖了竞争。

* When invoked by nodes that appear at the point of call

* to be at the head of the stack, calls to park are

* preceded by spins to avoid blocking when producers and

* consumers are arriving very close in time. This can

* happen enough to bother only on multiprocessors.

*

当awaitFulfill被栈头节点调用时,通过自旋park一段时间,以免在刚要阻塞的时刻,

有生产者或消费者到达。这在多处理机上将会发生。

* The order of checks for returning out of main loop

* reflects fact that interrupts have precedence over

* normal returns, which have precedence over

* timeouts. (So, on timeout, one last check for match is

* done before giving up.) Except that calls from untimed

* SynchronousQueue.{poll/offer} don't check interrupts

* and don't wait at all, so are trapped in transfer

* method rather than calling awaitFulfill.

主循环检查返回的顺序将会反应,在正常返回时,中断是否处理,还是超时处理。

(在放弃匹配之前,及最后一次检查,正好超时),除非调用SynchronousQueue的

非超时poll/offer操作,不会检查中断,不等待,那么将调用transfer方法中的其他部分逻辑,

而不是调用awaitFulfill。

*/

long lastTime = timed ? System.nanoTime() : 0;

Thread w = Thread.currentThread();

SNode h = head;

//获取自旋的次数

int spins = (shouldSpin(s) ?

(timed ? maxTimedSpins : maxUntimedSpins) : 0);

for (;;) {

if (w.isInterrupted())

//如果线程被中断,则取消等待

s.tryCancel();

SNode m = s.match;

if (m != null)

//如果节点的匹配节点不为null,则返回匹配节点

return m;

if (timed) {

long now = System.nanoTime();

nanos -= now - lastTime;

lastTime = now;

if (nanos <= 0) {

//如果超时,则取消等待

s.tryCancel();

continue;

}

}

if (spins > 0)

//如果自旋次数大于零,且可以自旋,则自旋次数减1

spins = shouldSpin(s) ? (spins-1) : 0;

else if (s.waiter == null)

//如果节点S的等待线程为空,则设置当前节点为S节点的等待线程,以便可以park后继节点。

s.waiter = w; // establish waiter so can park next iter

else if (!timed)

//非超时等在者,park当前线程

LockSupport.park(this);

else if (nanos > spinForTimeoutThreshold)

//如果超时时间大于,最大自旋阈值,则超时park当前线程

LockSupport.parkNanos(this, nanos);

}

}

/**

* Returns true if node s is at head or there is an active

* fulfiller.

如果节点在栈头或栈头为FULFILLING的节点,则返回true

*/

boolean shouldSpin(SNode s) {

SNode h = head;

return (h == s || h == null || isFulfilling(h.mode));

}

/**

* Unlinks s from the stack.

*/

void clean(SNode s) {

s.item = null; // forget item

s.waiter = null; // forget thread

/*

* At worst we may need to traverse entire stack to unlink

* s. If there are multiple concurrent calls to clean, we

* might not see s if another thread has already removed

* it. But we can stop when we see any node known to

* follow s. We use s.next unless it too is cancelled, in

* which case we try the node one past. We don't check any

* further because we don't want to doubly traverse just to

* find sentinel.

最糟糕的情况是我们需要遍历整个栈,unlink节点s。如果有多个线程同时访问

clean方法,由于其他线程可能移除s节点,我们也许看不到s节点。但是我们可以停止

操作,当发现一个节点的后继为s。我们可以用s节点的后继,除非s节点取消,否则,

我们可越过s节点。我们不会进一步地检查,因为我们不想仅仅为了发现s节点,遍历两次。

*/

SNode past = s.next;

if (past != null && past.isCancelled())

past = past.next;

// Absorb cancelled nodes at head

SNode p;

while ((p = head) != null && p != past && p.isCancelled())

//设置栈头节点的后继为第一个非取消等待的节点

casHead(p, p.next);

// Unsplice embedded nodes,遍历栈,移除取消等待的节点

while (p != null && p != past) {

SNode n = p.next;

if (n != null && n.isCancelled())

p.casNext(n, n.next);

else

p = n;

}

}

// Unsafe mechanics

private static final sun.misc.Unsafe UNSAFE;

private static final long headOffset;

static {

try {

UNSAFE = sun.misc.Unsafe.getUnsafe();

Class k = TransferStack.class;

headOffset = UNSAFE.objectFieldOffset

(k.getDeclaredField("head"));

} catch (Exception e) {

throw new Error(e);

}

}

}

}

自此同步队列的TransferStack已经看完,由于同步队列的内容量较大,我们这一篇先分析到这,下一篇再看TransferQueue和其他部分,先小节一下吧:

SynchronousQueue阻塞队列,每次插入操作必须等待一个协同的移除线程,反之亦然。SynchronousQueue同步队列没有容量,可以说,没有一个容量。由于队列中只有在消费线程,尝试消费元素的时候,才会出现元素,所以不能进行peek操作;不能用任何方法,生产元素,除非有消费者在尝试消费元素,同时由于队列中没有元素,所以不能迭代。head是第一个生产线程尝试生产的元素;如果没有这样的生产线程,那么没有元素可利用,remove和poll操作将会返回null。SynchronousQueue实际一个空集合类。同时同步队列不允许为null。同步队列支持生产者和消费者等待的公平性策略。默认情况下,不能保证生产消费的顺序。如果一个同步队列构造为公平性,则可以线程以FIFO访问队列元素。当时非公平策略用的是TransferStack,公平策略用的是TransferQueue;TransferStack和TransferQueue是存放等待操作线程的描述,从TransferStack中Snode节点可以看出:节点关联一个等待线程waiter,后继next,匹配节点match,节点元素item和模式mode;模式由三种,REQUEST节点表示消费者等待消费资源,DATA表示生产者等待生产资源。FULFILLING节点表示生产者正在给等待资源的消费者补给资源,或生产者在等待消费者消费资源。当有线程take/put操作时,查看栈头,如果是空队列,或栈头节点的模式与要放入的节点模式相同;如果是超时等待,判断时间是否小于0,小于0则取消节点等待;如果非超时,则将创建的新节点入栈成功,即放在栈头,自旋等待匹配节点(timed决定超时,不超时);如果匹配返回的是自己,节点取消等待,从栈中移除,并遍历栈移除取消等待的节点;匹配成功,两个节点同时出栈,REQUEST模式返回,匹配到的节点元素(DATA),DATA模式返回返回当前节点元素)。如果与栈头节点的模式不同且不为FULFILLING,匹配节点,成功者,两个节点同时出栈,REQUEST模式返回,匹配到的节点元素(DATA),DATA(put)模式返回返回当前节点元素。如果栈头为FULFILLING,找出栈头的匹配节点,栈头与匹配到的节点同时出栈。从分析非公平模式下的TransferStack,可以看出一个REQUEST操作必须同时伴随着一个DATA操作,一个DATA操作必须同时伴随着一个REQUEST操作,这也是同步队列的命名中含Synchronous原因。这也应了这句话

SynchronousQueue像一个管道,一个操作必须等待另一个操作的发生。

SynchronousQueue解析下-TransferQueue:http://donald-draper.iteye.com/blog/2364842

0

0

分享到:

2017-03-21 22:08

浏览 2545

评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值