Java线程池的常用阻塞队列有哪些?

在Java并发编程中,线程池是一个非常重要的组件。线程池通过复用线程来优化性能,避免了频繁创建和销毁线程的开销。在Java中,线程池的实现主要依赖于java.util.concurrent包中的类,其中最常用的类是ThreadPoolExecutorThreadPoolExecutor在创建时可以指定不同的阻塞队列类型来存储待执行的任务。

LinkedBlockingQueue(无界队列)

源码解析

LinkedBlockingQueue 是一个基于链表实现的阻塞队列,其容量可以设置为Integer.MAX_VALUE,因此在大多数情况下可以视为无界队列。LinkedBlockingQueue在以下线程池中被广泛使用:

  • FixedThreadPool
  • SingleThreadExecutor

java

public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
    // 队列中最大容量
    private final int capacity;
    // 当前队列中元素个数
    private final AtomicInteger count = new AtomicInteger();
    // 队列头节点
    transient Node<E> head;
    // 队列尾节点
    transient Node<E> last;
    // 线程锁
    private final ReentrantLock putLock = new ReentrantLock();
    private final ReentrantLock takeLock = new ReentrantLock();
    // 非空和非满条件
    private final Condition notEmpty = takeLock.newCondition();
    private final Condition notFull = putLock.newCondition();

    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);
    }

    // 其他方法省略
}

应用场景

LinkedBlockingQueue适用于任务量较大但不会超出系统承受能力的场景。例如,一个大型电商平台的订单处理系统,每天会接收到大量的订单请求,这些请求需要被逐一处理和记录。

代码示例

java

import java.util.concurrent.*;

public class OrderProcessingSystem {

    public static void main(String[] args) {
        // 创建一个固定大小的线程池,核心线程数为10
        ExecutorService executor = Executors.newFixedThreadPool(10);
        // 使用LinkedBlockingQueue作为任务队列
        LinkedBlockingQueue<Runnable> orderQueue = new LinkedBlockingQueue<>();

        // 模拟订单生成
        for (int i = 0; i < 100; i++) {
            int orderId = i;
            orderQueue.add(() -> processOrder(orderId));
        }

        // 提交订单处理任务
        while (!orderQueue.isEmpty()) {
            executor.submit(orderQueue.poll());
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 模拟订单处理
    public static void processOrder(int orderId) {
        System.out.println("Processing order: " + orderId);
        // 模拟处理时间
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("Order processed: " + orderId);
    }
}

在这个示例中,我们创建了一个固定大小的线程池,并使用LinkedBlockingQueue来存储订单处理任务。由于LinkedBlockingQueue几乎不会被填满,可以确保所有订单都能被接受并处理。

SynchronousQueue(同步队列)

源码解析

SynchronousQueue 是一种特殊的阻塞队列,它没有容量,每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue常用于以下线程池:

  • CachedThreadPool

java

public class SynchronousQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
    // 内部节点
    static final class TransferStack<E> extends Transferer<E> {
        // 栈顶节点
        transient volatile SNode head;
        // 其他内部实现省略
    }

    private final Transferer<E> transferer;

    public SynchronousQueue() {
        this(false);
    }

    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }

    // 其他方法省略
}

应用场景

SynchronousQueue适用于任务量大且需要快速处理的场景,但需要注意避免资源耗尽。例如,一个高并发的聊天系统,每个消息都需要立即被处理并发送给接收者。

代码示例

java

import java.util.concurrent.*;

public class ChatSystem {

    public static void main(String[] args) {
        // 创建一个缓存线程池
        ExecutorService executor = Executors.newCachedThreadPool();
        // 使用SynchronousQueue作为任务队列
        SynchronousQueue<Runnable> messageQueue = new SynchronousQueue<>();

        // 模拟消息发送
        for (int i = 0; i < 50; i++) {
            int messageId = i;
            new Thread(() -> {
                try {
                    messageQueue.put(() -> processMessage(messageId));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }

        // 提交消息处理任务
        while (true) {
            try {
                executor.submit(messageQueue.take());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 模拟消息处理
    public static void processMessage(int messageId) {
        System.out.println("Processing message: " + messageId);
        // 模拟处理时间
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("Message processed: " + messageId);
    }
}

在这个示例中,我们创建了一个缓存线程池,并使用SynchronousQueue来存储消息处理任务。由于SynchronousQueue没有容量,所有消息都必须立即被处理或等待处理,这适用于高并发、低延迟的场景。

DelayedWorkQueue(延迟阻塞队列)

源码解析

DelayedWorkQueue 是一种专门用于处理延迟任务的阻塞队列,其内部采用堆数据结构来确保每次出队的任务都是当前队列中最早需要执行的任务。DelayedWorkQueue常用于以下线程池:

  • ScheduledThreadPool
  • SingleThreadScheduledExecutor

java

final class DelayedWorkQueue extends AbstractQueue<Runnable> implements BlockingQueue<Runnable> {
    private static final int INITIAL_CAPACITY = 16;
    private RunnableScheduledFuture<?>[] queue = new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
    private final ReentrantLock lock = new ReentrantLock();
    private int size = 0;
    // 其他内部实现省略

    public boolean offer(Runnable x) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            int i = size;
            if (i >= queue.length) grow(); // 扩容
            size = i + 1;
            if (i == 0) {
                queue[0] = (RunnableScheduledFuture<?>)x;
                setIndex(x, 0);
            } else {
                siftUp(i, (RunnableScheduledFuture<?>)x);
            }
            if (queue[0] == x) {
                leader = null;
                available.signal();
            }
        } finally {
            lock.unlock();
        }
        return true;
    }

    // 其他方法省略
}

应用场景

DelayedWorkQueue 适用于需要延迟或定时执行任务的场景。例如,一个定时任务系统,需要在特定的时间点执行各种任务,如发送邮件、生成报告等。

代码示例

java

import java.util.concurrent.*;

public class ScheduledTaskSystem {

    public static void main(String[] args) {
        // 创建一个定时任务线程池
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
        // 使用DelayedWorkQueue作为任务队列
        // 这里无需显式创建DelayedWorkQueue,因为ScheduledThreadPoolExecutor内部已经使用了它

        // 模拟定时任务
        for (int i = 0; i < 10; i++) {
            int taskId = i;
            executor.schedule(() -> executeTask(taskId), taskId * 10, TimeUnit.SECONDS);
        }

        // 关闭线程池
        executor.shutdown();
    }

    // 模拟任务执行
    public static void executeTask(int taskId) {
        System.out.println("Executing task: " + taskId);
        // 模拟处理时间
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("Task executed: " + taskId);
    }
}

在这个示例中,我们创建了一个定时任务线程池,并使用ScheduledExecutorService来提交定时任务。ScheduledExecutorService内部使用了DelayedWorkQueue,确保任务可以按照预定的时间执行。

总结

通过以上详细的实际应用场景和代码示例,我们可以更清楚地理解不同阻塞队列的特点和适用场景:

  1. LinkedBlockingQueue 适用于任务量大但不会超出系统承受能力的场景,如大型电商平台的订单处理系统。
  2. SynchronousQueue 适用于任务量大且需要快速处理的场景,如高并发的聊天系统。
  3. DelayedWorkQueue 适用于需要延迟或定时执行任务的场景,如定时任务系统。

理解这些阻塞队列的特点和工作原理,可以帮助我们在实际开发中更好地选择和使用线程池,提高系统的并发性能和稳定性。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程池阻塞队列是用来存储等待执行的任务的数据结构。当线程池中的线程都在执行任务时,新的任务会被放入阻塞队列中等待执行。当线程池中的线程空闲下来时,它们会从阻塞队列中取出任务并执行。 Java中常见的线程池阻塞队列有以下几种: 1. ArrayBlockingQueue:基于数组结构实现的FIFO阻塞队列。在构造该阻塞队列时需要指定队列的容量。当队列已满时,若再次进行数据写入操作,则线程将会进入阻塞,一直等待直到其他线程对元素进行消费。当队列为空时,对该队列的消费线程将会进入阻塞,直到有其他线程写入数据[^2]。 2. LinkedBlockingQueue:基于链表结构实现的FIFO阻塞队列。该队列的容量可以选择性地指定,如果不指定,默认大小为Integer.MAX_VALUE。当队列已满时,写入操作将会被阻塞,直到有其他线程从队列中取出元素。当队列为空时,读取操作将会被阻塞,直到有其他线程写入数据。 3. SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等待另一个线程的移除操作,反之亦然。当线程尝试插入元素时,如果没有其他线程正在等待移除元素,则插入操作将会失败。当线程尝试移除元素时,如果没有其他线程正在等待插入元素,则移除操作将会失败。 这些阻塞队列Java线程池中起到了重要的作用,可以根据实际需求选择适合的阻塞队列来管理任务的执行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值