转载请注明源出处:http://www.cnblogs.com/lighten/p/7515729.html

1.前言

  本章介绍阻塞队列SynchronousQueue。之前介绍过LinkedTransferQueue,特点提供了让生产者知道消费者消费了其产出,没消费就等待的模式,本章介绍的这个类则必须是生产者生产后消费者消费了才会继续下去,反之亦然,消费者必须等待生产者产出。SynchronousQueue只有这一种模式,而LinkedTransferQueue是可选的,SynchronousQueue不存储元素,像接力棒一样,没有交接就一直等。所以其特定是没有容量,不能peek查看,如果没有消费者不能插入,不能遍历,该队列表现的就像一个空的集合。同样的,该队列不接受空元素。默认情况下,线程的等待唤醒是非公平的,可以设置成公平模式,保证线程是先入先出(先到先得)。通常两种模式的性能差不多,非公平模式可以维持更多的线程,公平模式则支持更高的吞吐量。

2.SynchronousQueue

2.1 实现原理

  该类的实现是基于dual stack和dual queue算法,dual queue在LinkedTransferQueue中介绍过。queue和stack都包含数据节点和请求节点,其特点就是任何操作都能明确当前队列的所处模式(数据--没有被消费者消费或请求--没有生产者)。stack和queue都继承自抽象类Transferer,其定义了唯一方法transfer用来put或者take,在dual数据结构中,定义成一个方法原因在于put和take操作是对称的。

  SynchronousQueue的实现与原算法有些不同的地方:1、原算法使用bit-marked指针,这里使用mode bits,导致了一系列的改动。2、SynchronousQueue会阻塞线程等待到装满。3、通过超时和中断,支持取消操作,包括清除所有取消节点/线程,避免垃圾存留或内存损耗。

  阻塞操作大多通过LockSupport类的park或unpark方法,除非是在多核CPU上该节点看起来是下一个首个填满的结点,通过自旋一位。在非常忙碌的队列中,自旋可以显著提升吞吐量。cleaning操作在queue和stack中不同,queue中remove操作是O(1)时间,但是stack为O(n)时间。

2.2 数据结构

  Transferer就是上面所说的抽象类,里面只有一个方法。后面也有Stack和Queue的实现。

  NCPUS:当前主机CPU核数

  maxTimedSpins:限时等待阻塞前自旋的次数,单核为0,多核32

  maxUntimedSpins:不限时等待阻塞前自旋的次数,maxTimedSpins * 16

  spinForTimeoutThreshold:纳秒数,这个为了更快的自旋而不是使用park时间。初略估计足够了,默认1000

  transferer:具体使用的实现对象。

  通过构造函数可以看出,公平模式使用的是queue,非公平模式使用的是stack,默认非公平。

2.3 基本操作

  该类的基本操作都是基于transferer实现的,所以这里就不进行介绍。

  存入取出的不同之处只在于第一参数是否是null,不为null就是存入,为null就是取出。所以该队列也不能存入null元素。其它的方法都是空。

2.4 TransferQueue

  数据结构和之前所讲LinkedTransferQueue基本一致,方法也类似。主要看transfer(E,boolean,long)方法。基本的算法就是循环做两件事情:1、如果队列为空或者持有相同的模式的结点,尝试添加队列结点,等待fulfilled或cancelled,并返回匹配项。2、如果队列不为空,放入的和其模式相反,即可以匹配就通过CAS操作填充该节点的item字段并出队列,返回匹配项。

  代码过长不给出,描述一下相关过长:

  1、通过E来判断当前调用是一个什么模式的结点。

  2、死循环处理:

    1.头尾节点存在null,为初始化进行循环。

    2.队列为空或模式一致:

      判断t是否是当前的尾,不是意味丢失尾,重新循环

      判断当前尾的下一个是否为null,不为null就是尾结点滞后了,重新设置尾结点,重新循环

      不等待就返回null

      创建该节点

      设置尾结点的下一个节点失败,被抢先,重新循环

      成功重置尾结点。

      进行等待指定时间。

      超时被取消,清除返回null。

      丢失顺序,重置头

      返回结果。

    3.队列不为空且模式不一致:

      头结点的下一个节点,如果为空或者头尾结点被改变了,读取不一致重新循环。

      此刻没有乱序,取出节点的item,进行CAS操作判断是否被抢先了,被抢先了移除该节点,继续循环尝试。

      成功了移除该节点,解除waiter的等待。

2.5 TransferStack

  stack的结点数据结构,和queue的有些不同,就是多了一个match结点。node有四个方法:1、CAS设置next结点。2、CAS设置match结点,返回匹配结构。3、取消当前结点。4、返回当前结点是否取消。

  transfer方法的逻辑和queue的类似,stack的transfer循环需要做三件事情:1、如果栈为空或者模式相同,生成结点入栈等待匹配,返回结果或空如果超时。2、如果栈不为空且模式不同,匹配等待的结点,两个都出栈,返回匹配值。由于其他线程可能执行第3点,匹配或者断开连接可能不是必须的。3、如果栈顶元素匹配成功,帮助其出栈匹配,然后继续循环。

  整个流程就是上面3点,其他的照着看代码应该较为简单,和queue的思路差不多。由于第三点需要帮助其他线程出栈,这个过程可能被其它后到线程抢先,所以是非公平的。

3.使用例子

    @Test
public void testSynchronous() {
SynchronousQueue<Integer> queue = new SynchronousQueue<>();
System.out.println(queue.offer(1)); // 立即返回,必须要有消费者
System.out.println(queue.poll()); // 立即返回,必须要有生产者
long start = System.currentTimeMillis();
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"-" +
queue.take()+",耗时:"+(System.currentTimeMillis()-start)); // 没有生产者一直阻塞
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"-" + queue.take());
Thread.sleep(1500);
System.out.println(Thread.currentThread().getName()+"-" + queue.poll(1, TimeUnit.SECONDS));
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
},"consumer").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"-等待2被消耗:"+queue.offer(2));
System.out.println(Thread.currentThread().getName()+"-等待3被消耗:");
long one = System.currentTimeMillis();
queue.put(3);
System.out.println("3被消耗,耗时:" + (System.currentTimeMillis() - one));
System.out.println(Thread.currentThread().getName()+"-等待4被消耗:" +
queue.offer(4, 1, TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"prodcuer").start();
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}

Java之集合(二十三)SynchronousQueue的更多相关文章

  1. Java从零开始学二十三(集合Map接口)

    一.Map接口 Collection.Set.List接口都属于单值的操作,即:每次只能操作一个对象,而Map与它们不同的是,每次操作的是一对对象,即二元偶对象,Map中的每个元素都使用key à v ...

  2. Java基础(二十三)集合(6)Map集合

    Map接口作为Java集合框架中的第二类接口,其子接口为SortedMap接口,SortedMap接口的子接口为NavigableMap接口. 实现了Map接口具体类有:HashMap(子类Linke ...

  3. 夯实Java基础(二十三)——Java8新特征之Stream API

    1.Stream简介 Java8中除了引入了好用的Lambda表达式.Date API之外,另外还有一大亮点就是Stream API了,也是最值得所有Java开发人员学习的一个知识点,因为它的功能非常 ...

  4. Java之集合(二十六)ConcurrentSkipListMap

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7542578.html 1.前言 一个可伸缩的并发实现,这个map实现了排序功能,默认使用的是对象自身的compa ...

  5. Java之集合(二)ArrayDeque

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7283928.html 1.前言 上章讲解了Java中的集合接口和相关实现抽象类,本章开始介绍一些具体的实现类,第 ...

  6. Java开发学习(二十三)----SpringMVC入门案例、工作流程解析及设置bean加载控制

    一.SpringMVC概述 SpringMVC是隶属于Spring框架的一部分,主要是用来进行Web开发,是对Servlet进行了封装.SpringMVC是处于Web层的框架,所以其主要的作用就是用来 ...

  7. Java基础(二十三)GUI图形界面编程(Java基础完)

    这里有我之前上课总结的一些知识点以及代码大部分是老师讲的笔记 个人认为是非常好的,,也是比较经典的内容,真诚的希望这些对于那些想学习的人有所帮助! 由于代码是分模块的上传非常的不便.也比较多,讲的也是 ...

  8. 201671010142 2017-2 《java第十二十三章学习感悟》

    Swing编程第一步,需要导入Swing相关包,即javax.swing.*. 接下里需要设置界面外观风格,使用到UIManager类. 设置完外观之后一定要调用 SwingUtilities.upd ...

  9. Java之集合(二十七)其它集合

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7551368.html 1.前言 本章介绍剩余的3个集合类:ConcurrentSkipListSet.CopyO ...

随机推荐

  1. 28. Bad Influence of Western Diet 西式饮食的消极影响

    28. Bad Influence of Western Diet 西式饮食的消极影响 ① The spread of Western eating habits around the world i ...

  2. Django入门指南-第7章:模板引擎设置(完结)

    http://127.0.0.1:8000/ <!--templates/home.html--> <!DOCTYPE html> <html> <head& ...

  3. Nginx中间件使用心得(一)

    一.Nginx简介 1.什么是Nginx? Nginx是一个高效.可靠的web服务器和反向代理中间件. (高效:支持海量并发请求,可靠:可靠运行的)      2.Nginx地位? 在https:// ...

  4. OpenGL中位图的操作(glReadPixels,glDrawPixels等)

    OpenGL中位图的操作 OpenGL中位图的操作(glReadPixels,glDrawPixels和glCopyPixels应用举例). 1. BMP文件格式简单介绍 BMP文件是一种像素文件,它 ...

  5. underscore chain

    //可以在面向对象或者函数的风格下使用Underscore_.map([1, 2, 3], function(n){ return n * 2; }); //一般习惯用这种 _([1, 2, 3]). ...

  6. Spring 配置 事务的几种方式

    Spring配置文件中关于事务配置总是由三个组成部分,DataSource.TransactionManager和代理机制这三部分,无论是那种配置方法,一般变化的只是代理机制这块! 首先我创建了两个类 ...

  7. 软件工程作业 - 实现WC功能(java)

    项目地址:https://github.com/yogurt1998/WordCount 要求 基本要求 -c 统计文件字符数(实现) -w 统计文件单词数(实现) -l 统计文件行数(实现) 扩展功 ...

  8. MongoDB Windowns 配置使用

     MongoDB 下载 MongoDB 提供了可用于 32 位和 64 位系统的预编译二进制包,你可以从MongoDB官网下载安装,MongoDB 预编译二进制包下载地址:https://www.mo ...

  9. requirejs的插件介绍与制作

    本文由作者郑海波授权网易云社区发布. 前言 我这里就不介绍requirejs了, 简而言之: requirejs是支持AMD规范的模块加载器, 事实上它也是AMD的最直接推动者. 现在可供挑选的开源模 ...

  10. BS4爬取物价局房产备案价以及dataframe的操作来获取房价的信息分析

    因为最近要买房子,然后对房市做了一些调研,发现套路极多.卖房子的顾问目前基本都是一派胡言能忽悠就忽悠,所以基本他们的话是不能信的.一个楼盘一次开盘基本上都是200-300套房子,数据量虽然不大,但是其 ...