【JDK源码分析系列】LinkedBlockingQueue源码分析

【JDK源码分析系列】LinkedBlockingQueue源码分析

【1】LinkedBlockingQueue 继承体系图示

【2】LinkedBlockingQueue 源码分析

【2.1】LinkedBlockingQueue 主要属性

链表结构相关属性

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    // 链表的作用是为了保存当前节点,节点中的数据可以是任意东西,
    // 若队列被应用到线程池时,节点就是线程
    // 若队列被应用到消息队列中,节点就是消息
    // 节点的含义主要看队列被使用的场景
    //
    // 链表节点
    static class Node<E> {
        // 链表节点中的数据元素
        E item;
        // 当前元素的下一个,为空表示当前节点是最后一个
        Node<E> next;
        // 节点构造函数
        Node(E x) { item = x; }
    }

    // 链表的容量,默认Integer.MAX_VALUE
    private final int capacity;

    // 链表已有元素大小,使用 AtomicInteger 线程安全
    private final AtomicInteger count = new AtomicInteger();

    // 链表头
    transient Node<E> head;

    // 链表尾
    private transient Node<E> last;
}

锁相关属性

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    // 设计两把锁的目的使得 take 和 put 可以同时进行
    // 取元素(take)时的锁
    private final ReentrantLock takeLock = new ReentrantLock();

    // 取元素(take)的条件队列
    private final Condition notEmpty = takeLock.newCondition();

    // 放元素(put)时的锁
    private final ReentrantLock putLock = new ReentrantLock();

    // 放元素(put)的条件队列
    private final Condition notFull = putLock.newCondition();
}

【2.2】LinkedBlockingQueue 构造函数

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    // 不指定容量,默认 Integer 的最大值
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    // 指定链表容量大小,链表头尾相等,节点值(item)都是 null
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

    // 已有集合数据进行初始化
    public LinkedBlockingQueue(Collection<? extends E> c) {
        // 默认初始化容量Integer.MAX_VALUE
        this(Integer.MAX_VALUE);
        // 获取写锁
        final ReentrantLock putLock = this.putLock;
        putLock.lock(); // Never contended, but necessary for visibility
        try {
            int n = 0;
            for (E e : c) {
                // 集合内的元素不能为空否则抛出异常
                if (e == null)
                    throw new NullPointerException();
                // capacity 代表链表的大小,在这里是 Integer 的最大值
                // 如果集合类的大小大于 Integer 的最大值,就会报错
                // 此处判断可以放在循环外部
                // 若给定集合大小是 1w,链表大小是 9k,
                // 按照现在代码实现,只能在 for 循环 9k 次时才能发现,
                // 则原来给定集合的大小已经大于链表大小,会导致 9k 次循环都是在浪费资源
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                // 入队,把新元素放到队尾
                enqueue(new Node<E>(e));
                ++n;
            }
            // 链表已有元素大小,使用 AtomicInteger 线程安全
            count.set(n);
        } finally {
            putLock.unlock();
        }
    }
}

【2.3】LinkedBlockingQueue 添加元素

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    public boolean offer(E e) {
        // 检查元素是否为null
        if (e == null) throw new NullPointerException();
        // 获取队列中已有的元素数量
        final AtomicInteger count = this.count;
        // 队列满了则直接返回
        if (count.get() == capacity)
            return false;
        // 预先设置c为-1,约定负数为新增失败
        int c = -1;
        // 新建节点
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            if (count.get() < capacity) {
                // 若队列未满则入队操作
                enqueue(node);
                // 新增计数赋值,注意这里 getAndIncrement 返回的是旧值
                // 这里的 c 是比真实的 count 小 1 的
                c = count.getAndIncrement();
                // 如果链表现在的大小小于链表的容量,说明队列未满
                // 可以尝试唤醒一个 notFull 的等待线程
                if (c + 1 < capacity)
                    notFull.signal();
            }
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            // c == 0,表示队列里有一个元素
            // 会尝试唤醒一个取元素(take)的等待线程
            signalNotEmpty();
        return c >= 0;
    }

    public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {
        // e 为空,抛出异常
        if (e == null) throw new NullPointerException();
        // 记录 timeout 时长
        long nanos = unit.toNanos(timeout);
        // 预先设置c为-1,约定负数为新增失败
        int c = -1;
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        // 设置可中断锁,加锁若线程中断则抛出异常
        putLock.lockInterruptibly();
        try {
            // 队列满了
            // 会阻塞一段时间后,继续运行
            while (count.get() == capacity) {
                // 如果当前等待的时间小于等于 0 ,说明已经超过等待时间了
                // 直接返回 false
                if (nanos <= 0)
                    return false;
                // 阻塞等待 nanos 时间,如果超过这段时间,返回的值小于等于 0
                nanos = notFull.awaitNanos(nanos);
            }
            // 队列没有满,直接新增到队列的尾部
            enqueue(new Node<E>(e));
            // 新增计数赋值,注意这里 getAndIncrement 返回的是旧值
            // 这里的 c 是比真实的 count 小 1 的
            c = count.getAndIncrement();
            // 如果链表现在的大小小于链表的容量,说明队列未满
            // 可以尝试唤醒一个 notFull 的等待线程
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            // 释放锁
            putLock.unlock();
        }
        if (c == 0)
            // c == 0,表示队列里有一个元素
            // 会尝试唤醒一个取元素(take)的等待线程
            signalNotEmpty();
        return true;
    }

    // 把e新增到队列的尾部
    // 如果有可以新增的空间的话,直接新增成功,否则当前线程陷入等待
    public void put(E e) throws InterruptedException {
        // e 为空,抛出异常
        if (e == null) throw new NullPointerException();
        // 预先设置c为-1,约定负数为新增失败
        int c = -1;
        // 新建节点
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        // 设置可中断锁,加锁线程中断则抛出异常
        putLock.lockInterruptibly();
        try {
            // 队列满了
            // 当前线程阻塞,等待其他线程的唤醒
            // 其他线程 take 成功后就会唤醒此处被阻塞的线程
            while (count.get() == capacity) {
                // 当队列满的时候等待在 notFull 条件上
                notFull.await();
            }

            // 队列没有满,直接新增到队列的尾部
            enqueue(node);

            // 新增计数赋值,注意这里 getAndIncrement 返回的是旧值
            // 这里的 c 是比真实的 count 小 1 的
            c = count.getAndIncrement();

            // 如果链表现在的大小小于链表的容量,说明队列未满
            // 可以尝试唤醒一个 put 的等待线程
            if (c + 1 < capacity)
                notFull.signal();

        } finally {
            // 释放锁
            putLock.unlock();
        }
        // c==0,代表队列里面有一个元素
        // 会尝试唤醒一个取元素(take)的等待线程
        if (c == 0)
            signalNotEmpty();
    }

    // 入队,把新元素放到队尾
    private void enqueue(Node<E> node) {
        last = last.next = node;
    }
}

【2.4】LinkedBlockingQueue 获取元素

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    public E poll() {
        // count 代表当前链表数据的真实大小
        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();
                // 减一计算,注意 getAndDecrement 返回的值是旧值
                // c 比真实的 count 大 1
                c = count.getAndDecrement();
                // 如果队列里面有值,从取元素(take)的等待线程里面唤醒一个线程
                if (c > 1)
                    // 唤醒notEmpty条件等待的线程
                    notEmpty.signal();
            }
        } finally {
            takeLock.unlock();
        }
        // 如果队列空闲还剩下一个,尝试从放元素(put)的等待线程中唤醒一个线程
        if (c == capacity)
            signalNotFull();
        return x;
    }

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E x = null;
        // 默认负数,代表失败
        int c = -1;
        // 获取timeout
        long nanos = unit.toNanos(timeout);
        // count 代表当前链表数据的真实大小
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        // 设置可中断锁,加锁线程中断则抛出异常
        takeLock.lockInterruptibly();
        try {
            // 空队列时,阻塞,等待其他线程唤醒
            while (count.get() == 0) {
                // 如果当前等待的时间小于等于 0 ,说明已经超过等待时间了
                // 直接返回 false
                if (nanos <= 0)
                    return null;
                // 阻塞等待 nanos 时间,如果超过这段时间,返回的值小于等于 0
                nanos = notEmpty.awaitNanos(nanos);
            }
            // 非空队列,从队列的头部拿一个出来
            x = dequeue();
            // 减一计算,注意 getAndDecrement 返回的值是旧值
            // c 比真实的 count 大 1
            c = count.getAndDecrement();
            // 如果队列里面有值,从取元素(take)的等待线程里面唤醒一个线程
            if (c > 1)
                // 唤醒notEmpty条件等待的线程
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        // 如果队列空闲还剩下一个,尝试从放元素(put)的等待线程中唤醒一个线程
        if (c == capacity)
            signalNotFull();
        return x;
    }    

    // 阻塞拿数据
    public E take() throws InterruptedException {
        E x;
        // 默认负数,代表失败
        int c = -1;
        // count 代表当前链表数据的真实大小
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        // 设置可中断锁,加锁线程中断则抛出异常
        takeLock.lockInterruptibly();
        try {
            // 空队列时,阻塞,等待其他线程唤醒
            while (count.get() == 0) {
                // 等待在notEmpty条件
                notEmpty.await();
            }
            // 非空队列,从队列的头部拿一个出来
            x = dequeue();
            // 减一计算,注意 getAndDecrement 返回的值是旧值
            // c 比真实的 count 大 1
            c = count.getAndDecrement();

            // 如果队列里面有值,从取元素(take)的等待线程里面唤醒一个线程
            if (c > 1)
                // 唤醒notEmpty条件等待的线程
                notEmpty.signal();
        } finally {
            // 释放锁
            takeLock.unlock();
        }
        // 如果队列空闲还剩下一个,尝试从放元素(put)的等待线程中唤醒一个线程
        if (c == capacity)
            signalNotFull();
        return x;
    }

    // 队头中取数据
    // 链表头节点不存储数据
    private E dequeue() {
        // 取出头节点
        Node<E> h = head;
        // 头节点的下一个节点为 first
        Node<E> first = h.next;
        // 使 h 的下一个节点指向自己
        h.next = h; // help GC
        // 给链表头赋值
        head = first;
        // 取出链表头值
        E x = first.item;
        // 旧头节点指向 null,帮助 GC
        first.item = null;
        // 返回旧头节点值
        return x;
    }
}

【2.5】LinkedBlockingQueue 查看元素

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    // 查看并不删除元素,如果队列为空,返回 null
    public E peek() {
        // count 代表队列实际大小,队列为空,直接返回 null
        if (count.get() == 0)
            return null;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            // 拿到队列头
            Node<E> first = head.next;
            // 判断队列头是否为空并返回
            if (first == null)
                return null;
            else
                return first.item;
        } finally {
            takeLock.unlock();
        }
    }
}

致谢

本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。

【1】面试官系统精讲Java源码及大厂真题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值