在Java并发编程中,线程池是一个非常重要的组件。线程池通过复用线程来优化性能,避免了频繁创建和销毁线程的开销。在Java中,线程池的实现主要依赖于java.util.concurrent
包中的类,其中最常用的类是ThreadPoolExecutor
。ThreadPoolExecutor
在创建时可以指定不同的阻塞队列类型来存储待执行的任务。
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
,确保任务可以按照预定的时间执行。
总结
通过以上详细的实际应用场景和代码示例,我们可以更清楚地理解不同阻塞队列的特点和适用场景:
LinkedBlockingQueue
适用于任务量大但不会超出系统承受能力的场景,如大型电商平台的订单处理系统。SynchronousQueue
适用于任务量大且需要快速处理的场景,如高并发的聊天系统。DelayedWorkQueue
适用于需要延迟或定时执行任务的场景,如定时任务系统。
理解这些阻塞队列的特点和工作原理,可以帮助我们在实际开发中更好地选择和使用线程池,提高系统的并发性能和稳定性。