04.JUC 集合

Wesley13
• 阅读 744

##基本概念

LinkedBlockingQueue 是一个用链表实现的有界阻塞队列

LinkedBlockingQueue 按照先进先出的原则对元素进行排序。

LinkedBlockingQueue 采用了双锁、双条件队列来提高读写效率。


##内部构造

LinkedBlockingQueue 内部维护着一个单向链表,且链表头节点的值永远为空。如下图所示:

04.JUC 集合

下面来看它的构成:

  • Node ,节点

    static class Node { E item; Node next; Node(E x) { item = x; } }

  • 构造函数

    private transient Node head; private transient Node last; private final int capacity;

    public LinkedBlockingQueue() { // 该队列是有界的,若不指定容量,默认为最大值 this(Integer.MAX_VALUE); }

    public LinkedBlockingQueue(int capacity) { if (capacity <= 0){ // 抛出异常... } this.capacity = capacity; last = head = new Node(null); }


##双锁机制

由于采用了双锁机制,因此它的出队、入队操作可以同时进行,从而提高效率。

// 用于入队操作
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();

// 用于出队操作
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();

由于出入队可以同时进行,因此必须避免冲突问题。

  • 同一元素操作冲突:由于 LinkedBlockingQueue 采用了 FIFO(先进先出)的原则,实际上是双端操作,不会存在冲突。并且在其内部定义了两个变量:head、last。

    // 出队修改头节点 private transient Node head;

    // 入队修改尾节点 private transient Node last;

  • 元素数量操作冲突:因为入队数量要+1,出队数量要-1,因此需要保证它的可见性,这里采用了这里采用原子类来实现:

    private final AtomicInteger count = new AtomicInteger(0);


##入队操作

  • offer,该操作成功返回 true,失败返回 false。

    public boolean offer(E e) { if (e == null){ // 抛出异常... } // 判断元素的个数是否超过容量4 final AtomicInteger count = this.count; if (count.get() == capacity){ return false; } int c = -1; Node node = new Node(e); // 加锁 final ReentrantLock putLock = this.putLock; putLock.lock(); try { // 再次判断,存在等待获取锁期间,其他线程执行入队操作。 if (count.get() < capacity) { // 入队操作 enqueue(node); // 注意:数量+1,返回的旧值 c = count.getAndIncrement(); if (c + 1 < capacity){ // 队列未满,唤醒因为队列满而阻塞的线程 notFull.signal(); } } } finally { putLock.unlock(); } // 为 0 表示之前队列是空的,唤醒出队时因为空队列而进入 notEmpty 条件等待队列的线程 if (c == 0){ signalNotEmpty(); } return c >= 0; }

  • put,该操作成功返回 true,失败则进入阻塞。

    private final AtomicInteger count = new AtomicInteger(0);

    public void put(E e) throws InterruptedException {

    if (e == null){
        // 抛出异常...
    }
    
    int c = -1;
    Node<E> node = new Node(e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();
    
    try {
        
        // 满队列,进入条件等待队列,线程阻塞
        while (count.get() == capacity) {
            notFull.await();
        }
        
        // 关键-> 入队操作
        enqueue(node);
        
        c = count.getAndIncrement();
        if (c + 1 < capacity){
            notFull.signal();
        }
        
    } finally {
        putLock.unlock();
    }
    
    if (c == 0){
        signalNotEmpty();
    }
    

    }

  • 关键

    private void enqueue(Node node) { last = last.next = node; }

    private void signalNotEmpty() { final ReentrantLock takeLock = this.takeLock; takeLock.lock(); try { notEmpty.signal(); } finally { takeLock.unlock(); } }

  • 入队操作的过程如下所示:

04.JUC 集合


##出队操作

  • poll,成功返回被移除的元素,失败返回 null。

    public E poll() { final AtomicInteger count = this.count; if (count.get() == 0){ return null; } E x = null; int c = -1; // 加锁 final ReentrantLock takeLock = this.takeLock; takeLock.lock(); try { if (count.get() > 0) { // 关键 -> 出队 x = dequeue(); c = count.getAndDecrement(); if (c > 1){ notEmpty.signal(); } } } finally { takeLock.unlock(); } // 表示出队前满队列,唤醒因入队时满队列而进入 notFull 条件等待队列的线程。 if (c == capacity){ signalNotFull(); } return x; }

  • take,成功返回被移除的元素,失败则线程阻塞。

    public E take() throws InterruptedException { E x; int c = -1; final AtomicInteger count = this.count; final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); try { // 空队列,进入条件等待队列,线程阻塞 while (count.get() == 0) { notEmpty.await(); } x = dequeue(); c = count.getAndDecrement(); if (c > 1){ notEmpty.signal(); }
    } finally { takeLock.unlock(); } if (c == capacity){ signalNotFull(); } return x; }

  • 关键

    private E dequeue() { // 头节点、以及它的后继节点 Node h = head; Node first = h.next; // 等于 h.next = null,即断开后指针 h.next = h; // 设置新的头节点 head = first; E x = first.item; // 将节点的值置空 first.item = null; return x; }

    private void signalNotFull() { final ReentrantLock putLock = this.putLock; putLock.lock(); try { notFull.signal(); } finally { putLock.unlock(); } }

  • 出队过程如下图所示:

04.JUC 集合


点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
BlockingQueue介绍
几种类型的BlockingQueueArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。DelayQueue:一个使用优先级队列实现的无界阻塞队列。Synchro
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这