Java阻塞队列详解(原理种类及使用场景)

Java阻塞队列详解(原理种类及使用场景)-mikechen

Java阻塞队列是Java多线程并发编程的重要功能,下面就全面来详解Java阻塞队列的特点以及种类@mikechen

Java阻塞队列定义

Java中的阻塞队列,全称是Java Blocking Queue,是一种线程安全的队列实现,它提供了在队列为空或已满时进行阻塞等待的特性。

举个例子来说明一下吧:我们去餐馆吃饭,万一这家店生意好餐馆挤满了人,这时候肯定不能把顾客赶出去,于是餐馆就在旁边设置了一个休息等待区,这就是一个阻塞队列了。

 

Java阻塞队列详解(原理种类及使用场景)-mikechen

 

Java阻塞队列原理

Java阻塞队列的实现原理,主要包含如下5大部分:

1.底层数据结构

阻塞队列的底层通常采用数组或链表等数据结构。

比如:ArrayBlockingQueue使用数组,而LinkedBlockingQueue使用链表。

 

2.锁与条件变量

阻塞队列的实现通常使用锁和条件变量来控制并发访问,锁用于确保在对队列进行操作时的线程安全性,条件变量用于在队列为空或已满时进行阻塞和唤醒。

 

3.put 和 take 操作

阻塞队列的put操作(将元素放入队列)和take操作(从队列中取出元素)是核心操作。

Java阻塞队列详解(原理种类及使用场景)-mikechen

当队列满或空时,对应的操作会阻塞等待,直到满足条件。

 

4.阻塞和唤醒

在队列为空或已满时,线程会被阻塞。

 

5.CAS(Compare and Swap)操作

一些实现中可能使用了CAS操作来进行非阻塞的并发控制,从而提高性能,这通常用于无界队列。

 

Java阻塞队列有哪些

在Java中,主要的阻塞队列实现类包括:

1.ArrayBlockingQueue

这是一个基于数组实现的有界阻塞队列,需要指定队列的容量。

如下所示:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ArrayBlockingQueueExample {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5); // 创建容量为5的有界阻塞队列

        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    queue.put(i); // 将元素放入队列,如果队列已满会阻塞等待
                    System.out.println("Produced: " + i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    int value = queue.take(); // 从队列中取出元素,如果队列为空会阻塞等待
                    System.out.println("Consumed: " + value);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();

        producer.join();
        consumer.join();
    }
}

当队列已满时,生产者线程将会阻塞直到有空间可用,当队列为空时,消费者线程将会阻塞直到有元素可消费。

 

2.LinkedBlockingQueue

基于链表实现的可选有界或无界阻塞队列。

如下所示:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class LinkedBlockingQueueExample {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(); // 创建无界阻塞队列

        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    queue.put("Item " + i); // 放入元素,无界队列不会阻塞
                    System.out.println("Produced: Item " + i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    String item = queue.take(); // 取出元素,如果队列为空会阻塞等待
                    System.out.println("Consumed: " + item);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();

        producer.join();
        consumer.join();
    }
}

如果初始化时指定容量,则是有界的。如果没有指定容量,则默认为无界队列。

 

3.PriorityBlockingQueue

这是一个基于优先级堆实现的无界阻塞队列,元素根据优先级顺序进行排列

如下所示:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;

public class PriorityBlockingQueueExample {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> queue = new PriorityBlockingQueue<>(); // 创建无界优先级阻塞队列

        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    queue.put(i); // 放入元素,会根据优先级进行排序
                    System.out.println("Produced: " + i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    int value = queue.take(); // 取出元素,如果队列为空会阻塞等待
                    System.out.println("Consumed: " + value);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();

        producer.join();
        consumer.join();
    }
}

 

4.DelayQueue

支持延迟操作的阻塞队列,用于定时任务调度。

 

5.SynchronousQueue

容量为1的阻塞队列,用于直接传递元素。

当生产者线程放入一个元素后,它会等待消费者线程将元素取走,反之亦然。

 

6.LinkedTransferQueue

无界阻塞队列,支持异步传输。

它支持异步传输,即在元素未被消费之前,生产者和消费者线程可以异步执行。

 

Java阻塞队列使用场景

  1. 生产者-消费者模式: 阻塞队列非常适合实现生产者-消费者模式,其中生产者线程将数据放入队列,而消费者线程从队列中取出数据,以实现线程之间的解耦。
  2. 线程池: 阻塞队列常用于线程池的任务队列,用于存放等待执行的任务,保证线程池中的线程不会无限制地增长,同时控制任务的执行速度。
  3. 定时任务调度: DelayQueue可以用于实现定时任务调度,其中任务将在指定的延迟时间之后被执行。
  4. 多线程协调: 在多线程协调和通信的场景中,阻塞队列可以作为中介,帮助线程之间进行数据交换和通信。
  5. 数据缓冲: 阻塞队列可以用于实现数据缓冲,让生产者和消费者之间的速度差异得到平衡。

总之,Java阻塞队列在需要协调多线程工作、平衡资源利用、控制并发访问等方面都发挥着重要的作用。

评论交流
    说说你的看法