乐趣区

LinkedBlockingQueue-源码解读

LinkedBlockingQueue 源码解读

傻瓜源码 - 内容简介

傻瓜源码 - 内容简介
????【职场经验】(持续更新)
精编短文:如何成为值钱的 Java 开发 - 指南

如何日常学习、如何书写简历、引导面试官、系统准备面试、选择 offer、提高绩效、晋升 TeamLeader…..
????【源码解读】(持续更新) <br/>1. 源码选材:Java 架构师必须掌握的所有框架和类库源码<br/>2. 内容大纲:按照“企业应用 Demo”讲解执行源码:总纲“阅读指南”、第一章“源码基础”、第二章“相关 Java 基础”、第三章“白话讲源码”、第四章“代码解读”、第五章“设计模式”、第六章“附录 - 面试习题、相关 JDK 方法、中文注释可运行源码项目”
3. 读后问题:粉丝群答疑解惑
已收录:HashMap、ReentrantLock、ThreadPoolExecutor、《Spring 源码解读》、《Dubbo 源码解读》…..
????【面试题集】(持续更新)<br/>1. 面试题选材:Java 面试常问的所有面试题和必会知识点 <br/>2. 内容大纲:第一部分”注意事项“、第二部分“面试题解读”(包括:”面试题“、”答案“、”答案详解“、“实际开发解说”)
3. 深度 / 广度:面试题集中的答案和答案详解,都是对齐一般面试要求的深度和广度
4. 读后问题:粉丝群答疑解惑
已收录:Java 基础面试题集、Java 并发面试题集、JVM 面试题集、数据库 (Mysql) 面试题集、缓存 (Redis) 面试题集 …..
????【粉丝群】(持续更新) <br/>收录:阿里、字节跳动、京东、小米、美团、哔哩哔哩等大厂内推
???? 作者介绍:Spring 系源码贡献者、世界五百强互联网公司、TeamLeader、Github 开源产品作者
???? 作者微信:wowangle03(企业内推联系我)

  加入我的粉丝社群,阅读更多内容。从学习到面试,从面试到工作,从 coder 到 TeamLeader,每天给你答疑解惑,还能有第二份收入!

第 1 章 阅读指南

  • 本文基于 open-jdk 1.8 版本。
  • 本文根据”Demo“解读源码。
  • 本文建议分为两个学习阶段,掌握了第一阶段,再进行第二阶段;

    • 第一阶段,理解章节“源码解读”前的所有内容。即掌握 IT 技能:熟悉 LinkedBlockingQueue 原理。
    • 第二阶段,理解章节“源码解读”(包括源码解读)之后的内容。即掌握 IT 技能:精通 LinkedBlockingQueue 源码。
  • 建议按照本文内容顺序阅读(内容前后顺序存在依赖关系)。
  • 阅读过程中,如果遇到问题,记下来,后面不远的地方肯定有解答。
  • 阅读章节“源码解读”时,建议获得中文注释源码包配合本文,Debug 进行阅读学习。
  • 源码项目中的注释含义

    • “Demo”在源码中,会标注“// LinkedBlockingQueue Demo”。

第 2 章 LinkedBlockingQueue 简介

  LinkedBlockingQueue 是一个基于单向链表的、FIFO(先进先出)的阻塞队列。API 如下:

  • 向队列中添加元素:

    • void put():当队列满时,阻塞;
    • boolean offer():当队列满时,返回 false;
    • boolean offer(E e, long timeout, TimeUnit unit):如果队列满时,则阻塞 timeout unit 时间,如果规定时间内,仍然是满的,则返回 false;
    • boolean add():当队列满时,抛出异常;
  • 移除并返回队列头部元素:

    • E take():当队列为空时,则阻塞;
    • E poll():如果队列为空,则返回 null;
    • E poll(long timeout, TimeUnit unit):如果队列为空,则阻塞 timeout unit 时间,如果规定时间内,仍然没有数据可取,则返回 null;
  • 移除队列头部元素:

    • boolean remove(Object o):队列为空时,抛出异常。成功移除返回 true;
  • 获取队列头部元素:

    • E peek():返回队列头部元素,但并不移除。队列为空时,返回 null;

第 3 章 Demo

    public static void main(String[] args) throws InterruptedException {LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue<>();
        linkedBlockingQueue.put("元素 1");
        linkedBlockingQueue.offer("元素 2", 10L, TimeUnit.SECONDS);
        // 移除并返回队列头部元素;如果 linkedBlockingQueue 中不存在元素,则阻塞;// 打印结果:元素 1
        System.out.println(linkedBlockingQueue.take());
        // 移除并返回队列头部元素;如果 linkedBlockingQueue 中不存在元素,则阻塞 10 秒钟;10 秒钟后,如果仍然没有数据可取,则返回 null
        // 打印结果:元素 2
        System.out.println(linkedBlockingQueue.poll(10L, TimeUnit.SECONDS));// 元素 2
    }

第 4 章 源码基础

4.1 Node

  LinkedBlockingQueue 的内部类 Node,用来装载添加元素的,同时也是队列的组成元素。

代码示例 Node 成员变量

static class Node<E> {
    // item 表示当前节点存储的元素值,例如:"元素 1"
    E item;

    // next 指向后一个 Node 节点对象(代表后一个进入队列的元素,例:"元素 2")Node<E> next;
}

4.2 LinkedBlockingQueue

代码示例 LinkedBlockingQueue 重要成员变量

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    
    // head 记录头节点 Node 对象,头节点 Node 对象不装载任何数据,即 item 值为空;// 当要调用 take() 等出队方法时,会将第二个节点的 item 值取出作为返回值,然后将 item 置为空,替换为头节点。transient Node<E> head;

    // last 记录尾节点对象
    private transient Node<E> last;
    
    // count 记录 LinkedBlockingQueue 中的 Node 数量
    private final AtomicInteger count = new AtomicInteger();

    // capacity 表示 LinkedBlockingQueue 最大可容纳的 Node 数,默认是 Integer 的最大值
    private final int capacity;

4.3 队列生命周期

  1. 初始化:当调用 LinkedBlockingQueue 构造函数时进行初始化:会创建一个 Node 对象,赋值给 head 和 last 变量,作为 LinkedBlockingQueue 队列中的头节点和尾节点;该 Node 对象的 item 和 next 属性值都为空;
  2. 入队:当调用 put() 等入队方法添加数据 A 时,会先创建数据 A 的 Node 节点,将尾节点(尾节点就是 last 指向的 Node 节点)的 next 变量指向节点 A,然后将 last 变量也指向节点 A;
  3. 出队:当调用 take() 等出队方法取出头节点数据时,会先将头节点的下一个节点赋值给 head,记为新头节点;然后将新头节点的 item 值取出作为返回值,然后再将 item 置为空。

第 5 章 相关 Java 基础

5.1 ReentrantLock

  ReentrantLock 是 JDK 并发包(java.util.concurrent)中的可重入锁(可重入锁就是指持有锁的线程可以重复进入有该锁的代码块)。详见“ReentrantLock 源码解读”。

代码示例

    @Test
    public void testReentrantLock1() {
        // 多个线程使用同一个 ReentrantLock 对象,上同一把锁
        Lock lock = new ReentrantLock();
        try {
            // 本线程尝试获取锁;如果锁已经被其它线程持有,则会进入阻塞状态,直到获取到锁
            lock.lock();
            System.out.println("处理中...");
        } finally {
            // 释放锁
            lock.unlock();}
    }

5.2 Condition

  实现等待 / 通知机制,必须在 ReentrantLock 锁定的代码块中才能使用;与 Object 的 wait/notify 功能相似,但是支持更多功能。

  • void await():使当前线程进入阻塞状态,线程阻塞的同时会释放锁。
  • long awaitNanos(long nanosTimeout):使当前线程进入阻塞状态,线程阻塞的同时会释放锁;若指定时间内未被唤醒,则会自动取消阻塞,返回 nanosTimeout 减去实际阻塞的时间(如果值为 0 或负数,则说明是自动取消阻塞的,而不是调用 signal() 方法被唤醒的)。
  • void signal():唤醒处于等待中的 1 个线程(等待时间最长的线程)。阻塞线程被唤醒后,如果 ReentrantLock 对象的锁已被其它线程持有,则会重新进入阻塞状态(此线程仍然还被 Condition 视为等待时间最长的线程)。
    public static void main(String[] args) {
        // 多个线程上同一把锁
        Lock reentrantLock = new ReentrantLock();
        Condition condition = reentrantLock.newCondition();
        // 开启一个线程
        new Thread(() -> {
            try {System.out.println("线程 1 -lock");
                reentrantLock.lock();
                Thread.sleep(1000);
                System.out.println("线程 1 -await");
                condition.await();} catch (InterruptedException e) {e.printStackTrace();
            } finally {reentrantLock.unlock();
                System.out.println("线程 1 -unlock");

            }
        }).start();

        // 开启另一个线程
        new Thread(() -> {
            try {System.out.println("线程 2 -lock");
                reentrantLock.lock();
                System.out.println("线程 2 -signal");
                condition.signal();} finally {reentrantLock.unlock();
                System.out.println("线程 2 -unlock");
            }
        }).start();}

打印结果:

线程 1 -lock
线程 2 -lock
线程 1 -await
线程 2 -signal
线程 2 -unlock
线程 1 -unlock

5.3 AtomicInteger

  通过原子操作,实现线程安全的数值加减操作。

  • incrementAndGet():获取数值,并将数值 +1,最后返回加 1 的值。
  • getAndIncrement():获取数值,并将数值 +1,最后返回加 1 的值。

代码示例

public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(100);
    // 获取 AtomicInteger 的值,并累加 1,最后返回加 1 前的值。int b = atomicInteger.getAndIncrement();
    // 打印结果:100
    System.out.println(b);
}

public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(100);
    // 获取 AtomicInteger 的值,并累加 1,最后返回加 1 后的值。int b = atomicInteger.incrementAndGet();
    // 打印结果:101
    System.out.println(b);
}

<br/>

加入我的粉丝社群,阅读全部内容

  从学习到面试,从面试到工作,从 coder 到 TeamLeader,每天给你答疑解惑,还能有第二份收入,这样的知识星球,难道你还要犹豫!

退出移动版