首页 > 编程语言 >java15源码-LinkedBlockingQueue

java15源码-LinkedBlockingQueue

时间:2022-11-22 21:00:23浏览次数:35  
标签:java15 count capacity LinkedBlockingQueue 源码 线程 take put

一 类图

二 构造方法

// LinkedBlockingQueue.java

// take锁
    private final ReentrantLock takeLock = new ReentrantLock();
    
// put锁
    private final ReentrantLock putLock = new ReentrantLock();
    
public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity} is not greater
     *         than zero
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null); // 初始化单链表 头节点跟尾节点都是哑节点
    }

三 API

1 put

// LinkedBlockingQueue.java
public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        final int c;
        final Node<E> node = new Node<E>(e); // 单链表节点
        final ReentrantLock putLock = this.putLock; // 竞争put锁
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
            while (count.get() == this.capacity) {
                notFull.await(); // 队列满了不能再放元素了 将put线程阻塞在条件队列上 等待有线程take元素打破条件唤醒阻塞的put线程
            }
            this.enqueue(node); // 元素入队
            c = count.getAndIncrement(); // 元素入队完更新元素数量
            if (c + 1 < capacity)
                notFull.signal(); // 队列还没满 尝试唤醒曾经因为队列满了而阻塞等待的put线程
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            this.signalNotEmpty(); // 队列已经至少有一个元素了 尝试唤醒曾经因为队列空了而阻塞等待的take线程
    }
// LinkedBlockingQueue.java
private void enqueue(Node<E> node) { // 元素入队 操作单链表
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        last = last.next = node;
    }

2 take

// LinkedBlockingQueue.java
 public E take() throws InterruptedException {
        final E x;
        final int c;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock; // take锁
        takeLock.lockInterruptibly(); // 竞争take锁
        try {
            while (count.get() == 0) {
                this.notEmpty.await(); // 队列为空 让take线程阻塞在take锁的条件队列上 等待有其他线程put元素后唤醒这个take线程
            }
            x = dequeue(); // 元素出队操作
            c = count.getAndDecrement(); // 出队完成 更新队列中元素的数量
            if (c > 1)
                notEmpty.signal(); // 取完这个元素之后队列中依然还有元素可以取 尝试唤醒之前因为队列为空而阻塞的take线程
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull(); // 取完这个元素之后队列肯定就不是满的 尝试唤醒之前因为队列满而阻塞的put线程
        return x;
    }
private E dequeue() { // 元素出队 操作单链表
        // assert takeLock.isHeldByCurrentThread();
        // assert head.item == null;
        Node<E> h = head; // 单链表头节点
        Node<E> first = h.next; // 出队节点
        h.next = h; // help GC
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }

四 总结

LinkedBlockingQueue
数据结构 单链表
是否有界 是,可以不显示指定容量,默认值Integer.MAX_VALUE
锁实现 ReentrantLock
锁数量 2
线程阻塞机制 ReentrantLock条件队列阻塞/通知唤醒
生产者消费者用锁 生产者使用put锁操作链表尾,消费者使用take锁操作链表头

标签:java15,count,capacity,LinkedBlockingQueue,源码,线程,take,put
From: https://www.cnblogs.com/miss-u/p/16916432.html

相关文章

  • 源码安装mysql5.7
     参考:ubuntu安装mysqlserver&mysqlclientCentos6安装mysql5.5......
  • java15源码-ArrayBlockingQueue
    一阻塞队列APIThrowsexceptionSpecialvalueBlocksTimesoutInsertadd(e)offer(e)put(e)offer(e,time,unit)Removeremove()poll()take()poll(......
  • 1、ArrayList源码解析
    目录1概述2底层数据结构3构造函数4自动扩容5set()get()remove()6Fail-Fast机制1概述ArrayList实现了List接口,是顺序容器,允许放入null元素有一个容量(capacit......
  • forsage佛萨奇智能合约2.0源码解析
    本文由威-kaifa873整理发布,仅作为项目开发需求参考!飞机@sleu88TherearefivereasonsforjoiningFosaki:ItisacompletelyfairEthereumsmartcontract.Thefund......
  • 直播网站源码,uni-app 数据上拉加载更多功能
    直播网站源码,uni-app数据上拉加载更多功能打开项目根目录中的pages.json配置文件,为subPackages分包中的商品goods_list页面配置上拉触底的距离:"subPackages":[ ......
  • 下载go源码时,遇到git - error: RPC failed; curl 18 transfer closed with outstandin
    执行下条语句时,出现该错误gitclonehttps://go.googlesource.com/go解决方案:gitconfig--globalhttp.postBuffer524288000......
  • Seata 1.5.2 源码学习(事务执行)
    关于全局事务的执行,虽然之前的文章中也有所涉及,但不够细致,今天再深入的看一下事务的整个执行过程是怎样的。1.TransactionManagerio.seata.core.model.TransactionManag......
  • Android设计模式系列(7)--SDK源码之命令模式
    命令模式,在.net,java平台的事件机制用的非常多,几乎每天都与之打交道。android中对我印象最深的就是多线程多进程的环境,所以必然大量使用到Runbable,Thread,其实用的就是最......
  • Android设计模式系列(1)--SDK源码之组合模式
    Android设计模式系列(1)–SDK源码之组合模式Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用。在androidUI设计,几乎所有的widget和布局类......
  • 从源码的角度探究Activity的启动过程
    一.概述今天我们来搞一下底层一点的东西,大家可能对Activity的生命周期比较熟悉,但是一个Activity是如何启动起来的,你知道吗?今天就来探究一下。二.分析我们先随便写一个demo,然......