• LinkedBlockingQueue源码解析


    LinkedBlockingQueue

    LinkedBlockingQueue是一个可以指定容量大小的以单链表为数据结构实现的队列,默认容量大小为Integer.MAX_VALUE,可以在构造方法中传入容量大小。

    内部持有两个锁:读锁taskLock,控制元素的移除,对应条件notEmpty,如果队列空,则阻塞线程;

    写锁putLock,控制元素的新增,对应条件notFull,如果队满,则阻塞线程。其实都是写锁,暂且如此称呼,作为区分。

    当队列元素数量count达到队列容量大小capacity,则说明队满,count=0队空。

    链表内部类

    /**
     * 单链表
     */
    static class Node<E> {
        E item;
    
        /**
             * One of:
             * - the real successor Node
             * - this Node, meaning the successor is head.next
             * - null, meaning there is no successor (this is the last node)
             */
        Node<E> next;
    
        Node(E x) { item = x; }
    }
    

    关键属性

    // 队列容量
    private final int capacity;
    // 实际元素总数, count <= capacity
    private final AtomicInteger count = new AtomicInteger();
    // 头节点
    transient Node<E> head;
    // 尾节点
    private transient Node<E> last;
    // 取元素锁,读锁
    private final ReentrantLock takeLock = new ReentrantLock();
    // 当队列为空时,获取元素线程在该条件上等待
    private final Condition notEmpty = takeLock.newCondition();
    // 新增元素锁,写锁
    private final ReentrantLock putLock = new ReentrantLock();
    // 当队列满时,新增元素线程在该条件上等待
    private final Condition notFull = putLock.newCondition();
    

    构造方法

    /**
     * 无参构造,默认容量为Integer.MAX_VALUE 无界队列
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
    
    /**
     * 指定容量大小
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }
    

    新增元素

    /**
     * put新增元素
     * 1、如果队列满了,则在队满条件上等待,当队列不满条件满足,再次判断队列不满成立,向下执行
     * 2、队列不满,将新增元素入队,当前元素作为队列尾节点
     * 3、队列元素总数+1, 
     * 4、如果队列总数未满,唤醒在不满条件上等待的线程
     * 5、如果新增之前队列是空的,还需要唤醒在非空条件上等待的线程
     */
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            // 常规套路
            // 因为可能有多个线程在条件上等待,在唤醒某个等待线程的同时,有新的线程进来完成新增元素,
            // 这时如果唤醒线程不管不顾,继续向下执行,那么元素总数将会超过本该有的容量
            while (count.get() == capacity) {
                // 队列已满,进入等待
                notFull.await();
            }
            // 将节点入队
            enqueue(node);
            // 获取元素总数,并将元素总数+1
            // c = 新增后元素总数 - 1
            c = count.getAndIncrement();
            // 队列还未满,唤醒在队列满条件上等待的线程
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            // 释放写锁
            putLock.unlock();
        }
        // 如果队列新增元素前是空的,那么这里唤醒在队列空条件上等待的线程
        if (c == 0)
            signalNotEmpty();
    }
    
    // 入队节点,将新增节点作为尾节点的后驱节点,并更新新增节点为尾节点
    private void enqueue(Node<E> node) {
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        last = last.next = node;
    }
    
    /**
     * offer新增元素
     * 1、如果检测到队满,直接返回
     * 2、队未满,完成元素添加后,再次判断队列是否已满,如果未满,唤醒在队不满条件上等待的线程
     * 3、如果添加元素前队列空,则唤醒在队不空条件上等待的线程
     */
    public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        // 上来直接检测队列是否已满,如果队满,直接返回
        final AtomicInteger count = this.count;
        if (count.get() == capacity)
            return false;
        int c = -1;
        Node<E> node = new Node<E>(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();
        }
        if (c == 0)
            signalNotEmpty();
        return c >= 0;
    }
    
    

    移除元素

    /**
     * 队列空,则进入等待
     */
    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();
            // 元素个数减1
            c = count.getAndDecrement();
            // 队列中还有元素,唤醒在非空条件上等待的线程
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        // 如果移除元素前,队列是满的,还需要唤醒在队满条件上等待的线程
        if (c == capacity)
            signalNotFull();
        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
        // 将第一个队列元素置为队头,元素值设为null
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }
    
    /**
     * 队列空,返回null
     */
    public E poll() {
        final AtomicInteger count = this.count;
        // 队列空,直接返回null
        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();
                // 元素总数减1
                c = count.getAndDecrement();
                // 队列中还有元素,唤醒在非空条件上等待的线程
                if (c > 1)
                    notEmpty.signal();
            }
        } finally {
            takeLock.unlock();
        }
        // 如果移除元素前,队列是满的,还需要唤醒在队满条件上等待的线程
        if (c == capacity)
            signalNotFull();
        return x;
    }
    

    获取队列头元素

    /**
     * 仅是将队头元素返回,不会移除
     */
    public E peek() {
        if (count.get() == 0)
            return null;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            // 返回队头元素,实际是第二元素
            Node<E> first = head.next;
            // 如果为null,表明队列中没有元素,直接返回null
            if (first == null)
                return null;
            // 返回元素
            else
                return first.item;
        } finally {
            takeLock.unlock();
        }
    }
    

    remove移除元素

    public boolean remove(Object o) {
        if (o == null) return false;
        // 读锁写锁都上锁,防止其他线程新增或移除元素
        fullyLock();
        try {
            // 从队列头部移除元素
            for (Node<E> trail = head, p = trail.next;
                 p != null;
                 trail = p, p = p.next) {
                // 如果队列中有相等的,从队列链表中移除
                if (o.equals(p.item)) {
                    unlink(p, trail);
                    return true;
                }
            }
            return false;
        } finally {
            // 释放读锁和写锁
            fullyUnlock();
        }
    }
    
    void unlink(Node<E> p, Node<E> trail) {
        // assert isFullyLocked();
        // p.next is not changed, to allow iterators that are
        // traversing p to maintain their weak-consistency guarantee.
        p.item = null;
        trail.next = p.next;
        // 移除的是队尾元素,则修改队尾
        if (last == p)
            last = trail;
        // 如果移除元素前队满,则唤醒在队满条件上等待的线程
        if (count.getAndDecrement() == capacity)
            notFull.signal();
    }
    
    void fullyLock() {
        putLock.lock();
        takeLock.lock();
    }
    
    void fullyUnlock() {
        takeLock.unlock();
        putLock.unlock();
    }
    
    

    contains

    public boolean contains(Object o) {
        if (o == null) return false;
        // 读锁和写锁都上锁
        fullyLock();
        try {
            // 从队列链表头部开始遍历,查找是否有相等的元素
            for (Node<E> p = head.next; p != null; p = p.next)
                // 存在相等的元素
                if (o.equals(p.item))
                    return true;
            return false;
        } finally {
            // 释放读锁和写锁
            fullyUnlock();
        }
    }
    
  • 相关阅读:
    结对编程第一次作业
    软件工程第三次作业
    软件工程第二次作业
    软件工程第一次作业
    第五次作业(结对第2次)
    第四次作业
    第三次作业
    第二次作业(多图预警)
    第一次作业
    软工第四次作业——结对编程二
  • 原文地址:https://www.cnblogs.com/QullLee/p/12369775.html
Copyright © 2020-2023  润新知