07-多线程案例-任务调度
任务调度
挑战:在并发环境中,如何合理调度任务以提高效率和响应速度。
在并发环境中,合理调度任务以提高效率和响应速度是非常重要的。可以通过使用线程池(如 ExecutorService
)和合适的任务队列(如 BlockingQueue
)来管理任务的调度。下面给出几个使用场景和代码实例,帮助展示如何通过这些机制提高任务调度效率。
使用场景
1. 任务数量不确定时:
- 如果任务的数量和执行时间都不确定,使用线程池和
BlockingQueue
可以有效管理任务的生产和消费,避免线程过多导致的资源浪费,同时保证任务的按顺序执行。
2. 高并发任务的处理:
- 在需要处理大量并发请求时(如 Web 服务器或异步任务处理),通过线程池和任务队列能够高效地调度和处理这些任务。
3. 生产者-消费者问题:
- 在多线程环境下,使用
BlockingQueue
来实现生产者和消费者模式,可以确保生产者在任务队列满时等待,消费者在队列为空时等待,从而避免了资源的浪费和任务的丢失。
1. 使用 ExecutorService
和 BlockingQueue
管理任务队列
示例 1:生产者-消费者模式
在该示例中,生产者线程将任务放入 BlockingQueue
,而消费者线程则从队列中取出任务并执行。
import java.util.concurrent.*;class ProducerConsumerExample {private static final int QUEUE_CAPACITY = 10;public static void main(String[] args) throws InterruptedException {BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);// 创建一个线程池来处理任务ExecutorService executor = Executors.newFixedThreadPool(4);// 启动生产者线程executor.submit(new Producer(queue));// 启动消费者线程executor.submit(new Consumer(queue));// 等待一段时间后关闭线程池Thread.sleep(10000);executor.shutdown();}
}class Producer implements Runnable {private final BlockingQueue<Integer> queue;public Producer(BlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {try {int i = 0;while (true) {queue.put(i); // 将任务放入队列System.out.println("生产者生产任务: " + i);i++;Thread.sleep(500); // 模拟任务生产时间}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}class Consumer implements Runnable {private final BlockingQueue<Integer> queue;public Consumer(BlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {try {while (true) {Integer task = queue.take(); // 从队列中取出任务System.out.println("消费者消费任务: " + task);Thread.sleep(1000); // 模拟任务消费时间}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}
说明:
- 使用
ArrayBlockingQueue
作为任务队列,生产者不断向队列中添加任务,消费者则从队列中取任务并执行。 - 线程池管理生产者和消费者线程,避免了频繁创建线程带来的性能损耗。
2. 处理高并发任务
示例 2:高并发任务处理
在这个示例中,我们创建一个线程池并提交大量的任务,线程池根据系统资源自动分配线程,避免了线程过多导致的性能瓶颈。
import java.util.concurrent.*;class HighConcurrencyExample {public static void main(String[] args) throws InterruptedException {ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池// 提交大量任务for (int i = 0; i < 100; i++) {int taskId = i;executor.submit(() -> {System.out.println("任务 " + taskId + " 开始执行,线程:" + Thread.currentThread().getName());try {Thread.sleep(100); // 模拟任务执行} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("任务 " + taskId + " 执行完毕");});}// 关闭线程池executor.shutdown();}
}
说明:
- 通过创建一个固定大小的线程池
Executors.newFixedThreadPool(10)
,我们限制了并发线程数为 10。 - 线程池会根据任务的数量和执行时间合理调度线程,有效避免了线程过多导致的上下文切换和资源浪费。
3. 定时任务调度
示例 3:定时任务调度
使用 ScheduledExecutorService
来定期执行任务。例如,定时检查系统健康状况或者定期清理缓存。
import java.util.concurrent.*;class ScheduledTaskExample {public static void main(String[] args) {ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); // 创建一个定时任务线程池// 定时任务,每隔 2 秒执行一次executor.scheduleAtFixedRate(() -> {System.out.println("定时任务执行,时间:" + System.currentTimeMillis());}, 0, 2, TimeUnit.SECONDS);// 运行 10 秒后关闭线程池try {Thread.sleep(10000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}executor.shutdown();}
}
说明:
- 使用
scheduleAtFixedRate
方法安排一个定时任务,每隔 2 秒执行一次。 - 适用于需要定期执行任务的场景,如健康检查、日志清理等。
总结
ExecutorService
使得任务的调度更加高效,减少了线程创建和销毁的开销。BlockingQueue
用于生产者-消费者模式,合理控制任务的流入和流出,避免了资源浪费。ScheduledExecutorService
可以用来执行定时任务,适合需要周期性执行的场景。
通过合理使用线程池和任务队列,能够大大提高并发环境下任务的处理效率和响应速度。