【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门

简介: 前言 你是否在线程池工具类里看到过它的身影? 你是否会好奇LinkedBlockingQueue是啥呢? 没有关系,小手手点上关注,跟上主播的节奏。 什么是LinkedBlockingQueue? ...

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

📚欢迎订阅专栏,专栏名《在2B工作中寻求并发是否搞错了什么》

前言

你是否在线程池工具类里看到过它的身影?

public static ExecutorService newFixedThreadPool(int nThreads) {
   
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());

你是否会好奇LinkedBlockingQueue是啥呢?

没有关系,小手手点上关注,跟上主播的节奏。

什么是LinkedBlockingQueue?

LinkedBlockingQueue 是一个基于链表的线程安全阻塞队列,常用于生产者-消费者模式。

数据结构

  • 基于单向链表实现,队列头尾分别通过 head 和 last 指针维护。
  • 默认容量为 Integer.MAX_VALUE(近似无界队列),但可手动指定固定容量。

线程安全

  • 使用两把锁分离设计(入队锁 putLock 和出队锁 takeLock),提高并发性能。
  • 通过 ReentrantLock 和 Condition 实现阻塞(队列空时阻塞消费者,队列满时阻塞生产者)。

阻塞操作

  • put():队列满时阻塞生产者线程。
  • take():队列空时阻塞消费者线程。
  • 非阻塞方法:offer()(失败返回 false)、poll()(失败返回 null)

简单说说,和我们之前说的ArrayBlokcingQueue的区别:

特性 LinkedBlockingQueue ArrayBlockingQueue
底层结构 链表 数组
默认容量 Integer.MAX_VALUE(无界) 必须显式指定
锁机制 双锁分离(更高并发) 单锁控制
内存占用 动态扩展(链表节点开销) 预分配连续内存

简单使用LinkedBlockingQueue

因为我们的LinkedBlockingQueue也是实现了BlockingQueue的接口,所以下面的代码例子,会有这些方法。

image.png

构造方法

首先从构造方法说起吧,LinkedBlockingQueue有3个构造方法:

// 没有传任何参数,默认容量大小为Integer.MAX_VALUE
public LinkedBlockingQueue();

// 容量大小为入参
public LinkedBlockingQueue(int capacity)

// 容量大小为Integer.MAX_VALUE,集合中初始化元素为c
public LinkedBlockingQueue(Collection<? extends E> c)

添加元素入队操作

add(E e)方法:简单粗暴,非阻塞添加元素,队列满了的话直接抛IllegalStateException异常。

public static void main(String[] args) {
   
    BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);
    queue.add("A");
    queue.add("B"); // Exception in thread "main" java.lang.IllegalStateException: Queue full
}

offer(E e)方法:非阻塞添加元素,成功返回boolean,添加成功为true,添加失败为false

public static void main(String[] args) {
   
    BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);
    boolean result = queue.offer("A"); // true
    System.out.println(result);
    result = queue.offer("B"); // false
    System.out.println(result);
}

offer(E e, long timeout, TimeUnit unit)方法: 向队列添加元素。如果队列满了,就阻塞线程,等待一段时间,一段时间过后队列还是满的话,意味添加元素失败返回false,否则返回true。

public static void main(String[] args) throws InterruptedException {
    BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);
    boolean result = queue.offer("A", 1, TimeUnit.SECONDS); // true
    System.out.println(result);
    result = queue.offer("B", 1, TimeUnit.SECONDS); // false
    System.out.println(result);
}

但如果我们这样等待一会的话,执行结果就会不一样:

public static void main(String[] args) throws InterruptedException {
   
    BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);
    boolean result = queue.offer("A", 1, TimeUnit.SECONDS); // true
    System.out.println(result);

    // 启动另一个生产者线程
    new Thread(() -> {
   
        Boolean res;
        try {
   
            res = queue.offer("B", 2, TimeUnit.SECONDS); // 队列满了,阻塞2s等待
        } catch (InterruptedException e) {
   
            throw new RuntimeException(e);
        }
        System.out.println(res);       // true
    }).start();

    // 移除队列中的元素
    queue.poll();
}

put(E e)方法: 阻塞线程,直到队列不为空或中断者线程。

public static void main(String[] args) throws InterruptedException {
   
    BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);
    queue.put("A");
    queue.put("B"); // 队列已满,线程阻塞在这
}

移除元素出队操作

remove()方法:如果队列为空,直接抛出NoSuchElementException异常。

public static void main(String[] args) {
   
    BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);
    queue.remove(); // Exception in thread "main" java.util.NoSuchElementException
}

poll()方法:非阻塞获取队列头元素,如果队列为空,直接返回null。

public static void main(String[] args) {
   
    BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);
    String poll = queue.poll();
    System.out.println(poll); // null(队列中没有元素)
    queue.offer("A");      // 向队列中添加元素
    poll= queue.poll();
    System.out.println(poll); // A
}

poll(long timeout, TimeUnit unit)方法:阻塞一段时间获取队列中的元素,如果超过时间了,就队列还是为空,就返回null。

public static void main(String[] args) throws InterruptedException {
   
    BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);

    // 创建一个线程,3s后生产1个元素到队列中
    new Thread(() -> {
   
        try {
   
            Thread.sleep(2000);
            queue.offer("A");
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }).start();

    // 消费者,阻塞5s获取元素
    String poll= queue.poll(5 , TimeUnit.SECONDS);
    System.out.println(poll); // A
}

take()方法:阻塞线程获取队列中的元素,直到队列不为空,或者被其他线程中断,抛出异常停止。

public static void main(String[] args) throws InterruptedException {
   
    BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);

    // 创建一个消费者线程,获取队列中的元素
    Thread consumerThread = new Thread(() -> {
   
        try {
   
            String take = queue.take();     // 在这个案例中,线程会被一直阻塞到这,直到被中断
            System.out.println("消费者线程获取到元素:" + take);
        } catch (InterruptedException e) {
   
            System.out.println("线程者线程被中断了");
        }
    });
    consumerThread.start();
    // 隔3s后,中断消费者线程
    TimeUnit.SECONDS.sleep(1);
    consumerThread.interrupt();
}

输出结果:
线程者线程被中断了

检查队列中的元素

element()方法: 返回队列头的元素,但是如果队列为空,element方法会抛出NoSuchElementException异常。

public static void main(String[] args) throws InterruptedException {
   
    BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);
    queue.offer("A");
    queue.offer("B");
    System.out.println(queue.element());    // A
    queue.poll();   // 头元素出队
    System.out.println(queue.element());    // B
}

// 异常的情况
public static void main(String[] args) throws InterruptedException {
   
    BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);
    String element = queue.element();   // Exception in thread "main" java.util.NoSuchElementException
}

peek()方法:和element()方法差不多,返回队列头的元素,如果队列为空,会返回null

public static void main(String[] args) throws InterruptedException {
   
    BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);
    String element = queue.peek();
    System.out.println(element);    // null
}

后话

这就结束了?没有的,宝贝,没有的。

这里只是简答的使用,小手手点上关注,主播下一篇,直接开始看LinkedBlockingQueue源码。

目录
相关文章
|
14天前
|
存储 安全 Java
【Java并发】【原子类】适合初学体质的原子类入门
什么是CAS? 说到原子类,首先就要说到CAS: CAS(Compare and Swap) 是一种无锁的原子操作,用于实现多线程环境下的安全数据更新。 CAS(Compare and Swap) 的
47 15
【Java并发】【原子类】适合初学体质的原子类入门
|
11天前
|
缓存 安全 Java
【Java并发】【ConcurrentHashMap】适合初学体质的ConcurrentHashMap入门
ConcurrentHashMap是Java中线程安全的哈希表实现,支持高并发读写操作。相比Hashtable,它通过分段锁(JDK1.7)或CAS+synchronized(JDK1.8)实现更细粒度锁控制,提升性能与安全性。本文详细介绍其构造方法、添加/获取/删除元素等常用操作,并对比JDK1.7和1.8的区别,帮助开发者深入理解与使用ConcurrentHashMap。欢迎关注,了解更多!
47 3
【Java并发】【ConcurrentHashMap】适合初学体质的ConcurrentHashMap入门
|
14天前
|
Java
【源码】【Java并发】【LinkedBlockingQueue】适合中学体质的LinkedBlockingQueue入门
前言 有了前文对简单实用的学习 【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门 聪明的你,一定会想知道更多。哈哈哈哈哈,下面主播就...
46 6
【源码】【Java并发】【LinkedBlockingQueue】适合中学体质的LinkedBlockingQueue入门
|
15天前
|
安全 Java
【Java并发】【ArrayBlockingQueue】适合初学体质的ArrayBlockingQueue入门
什么是ArrayBlockingQueue ArrayBlockingQueue是 Java 并发编程中一个基于数组实现的有界阻塞队列,属于 java.util.concurrent 包,实现了 Bl...
50 6
【Java并发】【ArrayBlockingQueue】适合初学体质的ArrayBlockingQueue入门
|
15天前
|
安全 Java
【源码】【Java并发】【ArrayBlockingQueue】适合中学者体质的ArrayBlockingQueue
前言 通过之前的学习是不是学的不过瘾,没关系,马上和主播来挑战源码的阅读 【Java并发】【ArrayBlockingQueue】适合初学体质的ArrayBlockingQueue入门 还有一件事
44 5
【源码】【Java并发】【ArrayBlockingQueue】适合中学者体质的ArrayBlockingQueue
|
安全 Java Linux
Java LinkedBlockingQueue 实现
本文着重介绍 Java 并发容器中 LinkedBlockingQueue 的实现方式。
|
2月前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
214 60
【Java并发】【线程池】带你从0-1入门线程池
|
21天前
|
Java 中间件 调度
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
57 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
|
1月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
100 23
|
28天前
|
数据采集 存储 网络协议
Java HttpClient 多线程爬虫优化方案
Java HttpClient 多线程爬虫优化方案
下一篇
oss创建bucket