SynchronousQueue是这样一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。 
     不能在同步队列上进行 peek,因为仅在试图要取得元素时,该元素才存在; 
     除非另一个线程试图移除某个元素,否则也不能(使用任何方法)添加元素;也不能迭代队列,因为其中没有元素可用于迭代。队列的头是尝试添加到队列中的首个已排队线程元素; 如果没有已排队线程,则不添加元素并且头为 null。 
     对于其他 Collection 方法(例如 contains),SynchronousQueue 作为一个空集合。此队列不允许 null 元素。
    它非常适合于传递性设计,在这种设计中,在一个线程中运行的对象要将某些信息、 
事件或任务传递给在另一个线程中运行的对象,它就必须与该对象同步。 
    对于正在等待的生产者和使用者线程而言,此类支持可选的公平排序策略。默认情况下不保证这种排序。 
    但是,使用公平设置为 true 所构造的队列可保证线程以 FIFO 的顺序进行访问。 公平通常会降低吞吐量,但是可以减小可变性并避免得不到服务。 
    注意1:它一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。 
    注意2:它是线程安全的,是阻塞的。 
    注意3:不允许使用 null 元素。 
    注意4:公平排序策略是指调用put的线程之间,或take的线程之间。公平排序策略可以查考ArrayBlockingQueue中的公平策略。 
    注意5:SynchronousQueue的以下方法: 
    * iterator() 永远返回空,因为里面没东西。 
    * peek() 永远返回null。 
    * put() 往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走。 
    * offer() 往queue里放一个element后立即返回,如果碰巧这个element被另一个thread取走了,offer方法返回true,认为offer成功;否则返回false。 
    * offer(2000, TimeUnit.SECONDS) 往queue里放一个element但是等待指定的时间后才返回,返回的逻辑和offer()方法一样。 
    * take() 取出并且remove掉queue里的element(认为是在queue里的。。。),取不到东西他会一直等。 
    * poll() 取出并且remove掉queue里的element(认为是在queue里的。。。),只有到碰巧另外一个线程正在往queue里offer数据或者put数据的时候,该方法才会取到东西。否则立即返回null。 
    * poll(2000, TimeUnit.SECONDS) 等待指定的时间然后取出并且remove掉queue里的element,其实就是再等其他的thread来往里塞。 
    * isEmpty()永远是true。 
    * remainingCapacity() 永远是0。 
    * remove()和removeAll() 永远是false。

这是一个很有意思的阻塞队列,其中每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。因此此队列内部其 实没有任何一个元素,或者说容量是0,严格说并不是一种容器。由于队列没有容量,因此不能调用peek操作,因为只有移除元素时才有元素。

一个没有容量的并发队列有什么用了?或者说存在的意义是什么?

SynchronousQueue 的实现非常复杂,当然了如果真要去分析还是能够得到一些经验的,但是前面分析了过多的结构后,发现越来越陷于数据结构与算法里面了。我的初衷是通过研究并 发实现的原理来更好的利用并发来最大限度的利用可用资源。所以在后面的章节中尽可能的少研究数据结构和算法,但是为了弄清楚里面的原理,必不可免的会涉及 到一些这方面的知识,希望后面能够适可而止。

再回到话题。SynchronousQueue 内部没有容量,但是由于一个插入操作总是对应一个移除操作,反过来同样需要满足。那么一个元素就不会再SynchronousQueue 里面长时间停留,一旦有了插入线程和移除线程,元素很快就从插入线程移交给移除线程。也就是说这更像是一种信道(管道),资源从一个方向快速传递到另一方 向。

需要特别说明的是,尽管元素在SynchronousQueue 内部不会“停留”,但是并不意味之SynchronousQueue 内部没有队列。实际上SynchronousQueue 维护者线程队列,也就是插入线程或者移除线程在不同时存在的时候就会有线程队列。既然有队列,同样就有公平性和非公平性特性,公平性保证正在等待的插入线 程或者移除线程以FIFO的顺序传递资源。

显然这是一种快速传递元素的方式,也就是说在这种情况下元素总是以最快的方式从插入着(生产者)传递给移除着(消费者),这在多任务队列中是最快处理任务的方式。在线程池的相关章节中还会更多的提到此特性。

SynchronousQueue<E>的定义如下

public class SynchronousQueue<E>
extends AbstractQueue<E>
implements BlockingQueue<E>, Serializable

从上面可以看出,它实现BlockingQueue<E>,所以是阻塞队列,从名字看,它又是同步的。

它模拟的功能类似于生活中一手交钱一手交货这种情形,像那种货到付款或者先付款后发货模型不适合使用SynchronousQueue。
首先要知道SynchronousQueue没有容纳元素的能力,即它的isEmpty()方法总是返回true,但是给人的感觉却像是只能容纳一个元素。

另外在创建SynchronousQueue时可以传递一个boolean参数来指定它是否是访问它的线程按遵守FIFO顺序处理,true表示遵守FIFO。

下面使用SynchronousQueue模拟只能生产一个产品的生产者-消费者模型

import java.util.Random;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit; public class Test19 {
public static void main(String[] args) {
SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
new Customer(queue).start();
new Product(queue).start();
}
static class Product extends Thread{
SynchronousQueue<Integer> queue;
public Product(SynchronousQueue<Integer> queue){
this.queue = queue;
}
@Override
public void run(){
while(true){
int rand = new Random().nextInt(1000);
System.out.println("生产了一个产品:"+rand);
System.out.println("等待三秒后运送出去...");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.offer(rand);
}
}
}
static class Customer extends Thread{
SynchronousQueue<Integer> queue;
public Customer(SynchronousQueue<Integer> queue){
this.queue = queue;
}
@Override
public void run(){
while(true){
try {
System.out.println("消费了一个产品:"+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------------------------------------------");
}
}
}
/**
* 运行结果:
* 生产了一个产品:464
等待三秒后运送出去...
消费了一个产品:773
------------------------------------------
生产了一个产品:547
等待三秒后运送出去...
消费了一个产品:464
------------------------------------------
生产了一个产品:87
等待三秒后运送出去...
消费了一个产品:547
------------------------------------------
*/
}

  

SynchronousQueue 的简单应用的更多相关文章

  1. JDK1.5新特性,基础类库篇,集合框架(Collections)

    集合框架在JDK1.5中增强特性如下: 一. 新语言特性的增强 泛型(Generics)- 增加了集合框架在编译时段的元素类型检查,节省了遍历元素时类型转换代码量. For-Loop循环(Enhanc ...

  2. Android Java 线程池 ThreadPoolExecutor源代码篇

    线程池简单点就是任务队列+线程组成的. 接下来我们来简单的了解下ThreadPoolExecutor的源代码. 先看ThreadPoolExecutor的简单类图,对ThreadPoolExecuto ...

  3. Queue API的几种实现详解

    目录 Queue API的几种方法的使用 ArrayBlockingQueue原理及源码解析 ArrayBlockingQueue的成员变量 ArrayBlockingQueue的offer和put方 ...

  4. 【实战Java高并发程序设计 7】让线程之间互相帮助--SynchronousQueue的实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  5. 【JUC】JDK1.8源码分析之SynchronousQueue(九)

    一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...

  6. Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池

    前言:由于最近在做SDK的功能,需要设计线程池.看了很多资料不知道从何开始着手,突然发现了AsyncTask有对线程池的封装,so,就拿它开刀,本文将从AsyncTask的基本用法,到简单的封装,再到 ...

  7. 从一个简单的Java单例示例谈谈并发

    一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这么写 public class UnsafeLazyInitiallization { private static Un ...

  8. ExecutorService与Executors例子的简单剖析

    对于多线程有了一点了解之后,那么来看看java.lang.concurrent包下面的一些东西.在此之前,我们运行一个线程都是显式调用了Thread的start()方法.我们用concurrent下面 ...

  9. ExecutorService与Executors例子的简单剖析(转)

    对于多线程有了一点了解之后,那么来看看java.lang.concurrent包下面的一些东西.在此之前,我们运行一个线程都是显式调用了 Thread的start()方法.我们用concurrent下 ...

随机推荐

  1. Hadoop Streaming框架使用(一)

      Streaming简介 link:http://www.cnblogs.com/luchen927/archive/2012/01/16/2323448.html Streaming框架允许任何程 ...

  2. spring框架设计理念(上)

    一.前言    spring的应用非常的广泛,在开发过程中我们经常接触,可能会有一种感觉:对spring即熟悉又陌生,熟悉体现在我们几乎每天都在使用,对spring的IOC.AOP功能都有了基本的了解 ...

  3. BZOJ 1059 & 二分图匹配

    题意: 判断一个黑白染色的棋盘能否通过交换行或列使对角线上都是黑色. SOL: 真是有点醉...这种问题要么很神要么很水...第一眼感觉很水但就是不造怎么做...想了10分钟怎么感觉就是判断个数够不够 ...

  4. ACM 汉诺塔(三)

    汉诺塔(三) 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度 ...

  5. ACM Coin Test

    Coin Test 时间限制:3000 ms  |  内存限制:65535 KB 难度:1   描述 As is known to all,if you throw a coin up and let ...

  6. Node.js的DES加解密和MD5加密

    最基本的就是经常用的md5加密算法 代码如下 var  MD5=function (data) {        var _encrymd5 = require('crypto').createHas ...

  7. Oracle数据库合并行记录,WMSYS.WM_CONCAT 函數的用法

    Sql代码 select t.rank, t.Name from t_menu_item t; 10 CLARK    10 KING    10 MILLER    20 ADAMS    20 F ...

  8. 对AFN的二次封装

     .h .m

  9. Oracle函数解析

    一:大小写控制函数 lower()函数:(此函数将全部的大写字母都可以变为小写字母) upper()函数:(将输入的字符串变为大写字母) initcap()函数:(将每个字符串的首字母大写)  二:字 ...

  10. 四种读写方案IO流 (JAVA)

    File类用于访问文件或目录的属性 流:指一连串流动的字符,是以先进先出的方式发送信息的通道.程序和数据源之间是通过流联系起来的. 第一套:字节流读取写入方案 FileInputStream :字节流 ...