首页 > 编程语言 >【Java 并发】【十】【JUC数据结构】【六】SynchronousQueue同步阻塞队列原理

【Java 并发】【十】【JUC数据结构】【六】SynchronousQueue同步阻塞队列原理

时间:2023-04-09 21:22:57浏览次数:40  
标签:JUC Java 消费者 SynchronousQueue 阻塞 生产者 线程 null 数据

1  前言

看过了LinkedBlockingQueue、ArrayBlockingQueue、DelayQueue等阻塞队列,这节我们又要看一个不一样的队列,SynchronousQueue同步阻塞队列。

2  SynchronousQueue是什么

SynchronousQueue的同步队列,使用的场景比较少,主要是用来做线程之间的数据同步传输的。
线程之间的同步数据传输是什么意思?我看LinkedBlockingQueue、ArrayBlockingQueue不也是使用在线程之间传输数据吗,生产者线程往队列里面存入数据,消费者线程从队列里面取出数据,进行线程之间的数据传输。
确实是这样的,LinkedBlockingQueue、ArrayBlockingQueue、DelayQueue之类的阻塞队列,有一个容器的概念,生产者只需要往容器里面存入数据就完事了,如下图所示:

这里的图,生产者线程和消费者线程没有之间的交互;生产者只负责把数据放入容器中,消费者只负责从容器中取出数据进行消费。这种模型类似于生产者通过容器将数据间接的传递给了消费者。
然而SynchronousQueue的模型是不一样的,生产者和消费者的数据传输不是通过容器来的,而SynchronousQueue内部没有存储数据的容器。
(1)生产者是直接将数据传递给消费者的,通过手递手的方式来进行传递,同时消费者线程直接从生产者线程手中取得数据。
(2)如果生产者线程A传输过程中,发现没有消费者线程获取自己的数据,自己则阻塞等待;直到有消费者线程B取数据的时候唤醒生产者线程A,然后生产者线程A亲手数据交给消费者线程B。
(3)同理,如果消费者线程B取数据的时候,发现没有生产者线程传递数据过来,自己则阻塞等待;直到有生产者线程A传递数据的时候唤醒消费者线程B,然后生产者线程A亲手数据交给消费者线程B。
就类似如下的模型:

(1)如上图所示:生产者线程A、线程B在传递数据给消费者的时候;发现此时没有消费者线程取数据,此时自己进入阻塞队列阻塞等待。
(2)同理消费者线程C、线程D在取数据的时候,发现没有生产者线程传递数据过来,此时消费者线程进入阻塞队列阻塞等待。

我们继续,假设首先生产者线程A、线程B在传递数据的时候发现没有消费者线程取,自己阻塞等待。然后过一段时间之后,当消费者线程B、线程C来取数据就会得到如下图形:

(1)线程A、线程B在传递数据的时候发现没有消费者线程取数据,这个时候,线程A、线程B进入阻塞队列阻塞等待
(2)等一段时间之后,线程C来取数据了,这个时候发现已经有生产者在等着把数据给我了,这个时候我唤醒生产者线程A,然后线程A把数据传给线程C。

同样的道理,假设最开始是线程C、线程D来取数据,但是发现没有生产者传递数据过来,自己就会进入阻塞等待;但是一段时间之后生产者线程A、线程B来传递数据过来,就会唤醒线程C、线程D,然后亲手把数据传给线程C、线程D,就会得到如下图形:

(1)消费者线程C、线程D取数据发现,没有生产者给传数据给它们;然后就进入阻塞队列阻塞等待
(2)生产者线程A要进行数据传递,检查发现等待队列已经有人在等待取数据了,此时唤醒线程C,然后将数据交给线程C

那么通过上述我们大概可以总结下:

(1)首先SynchronousQueue队列是没有存储数据的容器的,要进行数据传输,必须是要进行手递手传递,也就是生产者必须亲手传递给消费者
(2)其次是生产者和消费者必须一一匹配,如果只有生产者传递数据,此时需要进入等待队列阻塞等待,然后由消费者将其唤醒;同样只有消费者取数据也需要在阻塞队列等待,然后由生产者将其唤醒。

我们接下来就来看看具体是如何实现的。

3  SynchronousQueue内部源码

我们来看下SynchronousQueue的各个方法的源码:

3.1  put方法

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    // 调用底层的transfer表示要进行数据传输,e 是要进行传输的元素
    // false表示不可设置阻塞时间,也就是如果没消费者取数据,则一直阻塞等待
    // 0 表示一直阻塞等待
    if (transferer.transfer(e, false, 0) == null) {
        // 如果返回null,说明线程被中断或者超时了
        Thread.interrupted();
        throw new InterruptedException();
    }
}

3.2  take方法

public E take() throws InterruptedException {
    // 和put方法一样,取数据的时候也是调用transfer方法
    // null表示自己是进行取数据操作,如果传递不是null则是数据传输操作
    // false表示如果没有生产者传输数据,则一直阻塞等待
    // 0 表示一直阻塞等待
    E e = transferer.transfer(null, false, 0);
    if (e != null)
        // 如果拉取到的数据非null,说明取数据成功,返回数据
        return e;
    // 取数据失败,线程被中断了
    Thread.interrupted();
    throw new InterruptedException();
}

上面的源码我们得出,生产者调用transfer来进行数据传输的,传递的数据为e;消费者通过调用transfer来获取数据,参数null表示取数据。生产者和消费者调用同样的方法,操作不同通过参数是否为null来区分。

3.3  offer(E e)方法

public boolean offer(E e) {
    // 检查传递的元素e非null,如果是null则抛出异常
    if (e == null) throw new NullPointerException();
    // e表示要传递的数据
    // true && 0 表示如果没有消费者取数据下,自己要阻塞等待的时间为0纳秒
    // 也就是说如果没有消费者取数据,马上返回传输失败
    return transferer.transfer(e, true, 0) != null;
}

offer(E e, long timeout, TimeUnit unit)方法:

public boolean offer(E e, long timeout, TimeUnit unit)
    throws InterruptedException {
    // 检查要传递的数据e非null,如果是null抛出异常
    if (e == null) throw new NullPointerException();
    // e表示要进行传输的数据
    // true && timeout表示如果没有消费者取数据的情况下
    // 最多等待timeout的时间,如果超时还是没有消费者取数据,就返回失败
    if (transferer.transfer(e, true, unit.toNanos(timeout)) != null)
        return true;
    if (!Thread.interrupted())
        return false;
    throw new InterruptedException();
}

3.4  poll方法

public E poll() {
    // 直接调用transfer方法取数据
    // 传递参数null表示自己的操作是消费者取数据,区分生产者的传递操作
    // true && 0 表示如果此时没有生产者传输数据过来,则立马返回取数据失败
    return transferer.transfer(null, true, 0);
}

poll(long timeout, TimeUnit unit)方法:

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    // 调用transfer方法进行取数据
    // null表示自己的操作是消费者取数据,区分生产者传输数据操作
    // true && timeout 表示如果取数据的时候没有生产者发送数据过来
    // 最多阻塞等待timeout的时间,超过了这个时间还是没有生产者发送数据过来,抛出中断异常
    E e = transferer.transfer(null, true, unit.toNanos(timeout));
    if (e != null || !Thread.interrupted())
        return e;
    throw new InterruptedException();
}

SynchronousQueue同步等待队列的源码精髓就在于transfer方法里面了,生产者传输数据、消费者取数据都是通过transfer方法来实现的。

4  小结

到这里,SynchronousQueue延迟队列就看的差不多了,这个阻塞队列的使用场景其实并不多,大多数是在线程之间的同步数据传递场景,有理解不对的地方欢迎指正哈。

标签:JUC,Java,消费者,SynchronousQueue,阻塞,生产者,线程,null,数据
From: https://www.cnblogs.com/kukuxjx/p/17300910.html

相关文章

  • javaScript技巧
    一、动态修改网页内容或属性1.改变HTML内容 2.改变属性值  3.改变css样式  4.操作元素  5.定时器 ......
  • Java8 Stream
    Java8Stream1Stream概述2Stream的创建3Stream的使用案例使用的员工类3.1遍历/匹配(foreach/find/match)3.2筛选(filter)3.3聚合(max/min/count)3.4映射(map/flatMap)3.5归约(reduce)3.6收集(collect)3.6.1归集(toList/toSet/toMap)3.6.2统计(count/averag......
  • 【Java 并发】【十】【JUC数据结构】【五】DelayQueue延迟阻塞队列原理
    1 前言前两节我们看了BlockingQueue阻塞队列的两个子类,LinkedBlockingQueue、ArrayBlockingQueue,它们都是使用了ReentrantLock、Condition的来实现的,在进行插入操作、拉取数据操作之前为了并发安全都需要进行加锁;然后插入时候在容量满的时候发现没有空间了,这时候调用Condition.......
  • JavaSE02基础语法
    JavaSE02基础语法1.注释​ 注释是对代码的解释和说明文字。Java中的注释分为三种:单行注释://这是单行注释文字多行注释:/*这是多行注释文字这是多行注释文字这是多行注释文字*/注意:多行注释不能嵌套使用。文档注释(暂时用不到):/**这是多行注释文字这是多行......
  • WebKit三件套(2):WebKit之JavaScriptCore/V8
    WebKit作为一个浏览器引擎,其中Javascript实现包括JavaScriptCore和V8,为了能更全面的了解WebKit,我们需要深入的了解Javascript实现的基本原理、其在WebKit中的作用以及与其他部分之间的交互,同时与Gecko中的Javacript实现作初步的对比。让我们开始了解WebKit之Javascript实现JavaScr......
  • java -- 练习题
    第一题1.定义一个Person类,要求有姓名和年龄,并且符合JavaBean标准,定义Student类继承Person,定义测试类,创建Student对象,要求创建Student对象的同时,指定Student对象的姓名为"张三",只能指定姓名不许指定年龄classPerson{privateStringname;privateintage;......
  • 几种常用的Java 算法
    packagejsh.mg.msg.service.msg.test;importjava.util.Arrays;importstaticjava.util.Arrays.binarySearch;/****几种常用的Java算法*/publicclassTestClass{/****二分查找算法*/publicstaticintbinarySearch(int[]arr,inttarge......
  • JS引擎(1):JS引擎擂台赛,JavaScript引擎的特征比较及术语科普
    上篇介绍过JavaScript引擎的历史,《JS引擎(0):起底各种JavaScript引擎群雄争霸之路》一些流行的JavaScript引擎SpiderMonkey ,BrendanEich在Netscape创建,由C/C++语言开发,可适配ECMA-262Edition5及其之后的标准版本Rhino,由NorrisBoyd(归属Netscape)创建,则是一个Ja......
  • JS引擎(2):Java平台上JavaScript引擎—Rhino/Nashorn概述
    可以后端开发的javascript引擎有ChromeV8基于C++java的Rhino引擎(JDK6被植入),Java8被替换为NashornRhino和Nashorn都是用Java实现的JavaScript引擎。它们自身都是普通的Java程序,运行在JVM上Rhino简介Rhino[ˈraɪnəʊ]是一种使用Java语言编写的JavaScript的......
  • JS引擎(0):JavaScript引擎群雄演义—起底JavaScript引擎
    JavaScript既是一个 面向过程的语言 又是一个 面向对象的语言。在JavaScript中,通过在运行时给空对象附加方法和属性来创建对象,与编译语言如C++和Java中常见的通过语法来定义类相反。对象构造后,它可以用作是创建相似对象的原型。JavaScript的动态特性包括运行时构造对......