ExecutorService 是 Java 中一个用于管理线程池和并发任务执行的框架。它是 java.util.concurrent 包的一部分。ExecutorService 提供了一种比使用 Thread 类更方便和灵活的方式来管理线程。
1. 基本概念
1.1 Executor 和 ExecutorService
- Executor:定义了一个 execute(Runnable command) 方法,用来执行提交的 Runnable 任务。
ExecutorService:继承了 Executor,提供了更多方法来管理和控制任务的执行,如 submit、shutdown、invokeAll 等。
1.2 线程池类型
- FixedThreadPool:定长线程池,可以控制线程最大并发数,多余的线程会在队列中等待。
- CachedThreadPool:可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- SingleThreadExecutor:单线程池,只有一个线程工作,确保所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
ScheduledThreadPool:定时线程池,用于执行定时任务或周期性任务。
2. 常用方法
2.1 任务提交方法
- execute(Runnable command):执行一个 Runnable 任务,但无返回值。
- submit(Runnable task):执行一个 Runnable 任务,并返回一个 Future 对象,可以通过 Future 对象获取任务的执行结果。
submit(Callable<T> task):执行一个 Callable 任务,并返回一个 Future 对象,Callable 有返回值。
2.2 任务调度方法(ScheduledExecutorService)
- schedule(Runnable command, long delay, TimeUnit unit):延迟执行任务。
- schedule(Callable<V> callable, long delay, TimeUnit unit):延迟执行任务并获取结果。
- scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):周期性执行任务,以固定频率执行。
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):周期性执行任务,以固定延迟执行。
2.3 关闭线程池
- shutdown():启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
shutdownNow():试图停止所有正在执行的任务,暂停处理等待的任务,并返回等待执行的任务列表。
3. 使用示例
3.1 基本线程池示例
有5个任务需要执行。
使用一个固定大小为3的线程池来并发执行这些任务。
每个任务执行的过程包括打印任务开始的信息、模拟执行2秒、打印任务完成的信息。public class BasicThreadPoolExample { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 1; i <= 5; i++) { executorService.submit(new Task(i)); //执行一个 Runnable 任务,并返回一个 Future 对象 } executorService.shutdown(); //启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 } } class Task implements Runnable { private final int taskId; Task(int taskId) { this.taskId = taskId; } @Override public void run() { System.out.println("第 " + taskId + " 线程, 线程名 " + Thread.currentThread().getName()); try { Thread.sleep(2000); // 模拟任务执行时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("第 " + taskId + " 线程, 线程名 " + Thread.currentThread().getName()+" 结束时间 "+ LocalDateTime.now()); } }
线程池中有3个线程 (pool-1-thread-1、pool-1-thread-2 和 pool-1-thread-3),每个线程可以同时执行一个任务。
前3个任务会立即被分配给线程池中的3个线程并开始执行。
每个任务执行2秒,然后完成,接着线程池中的线程会继续执行等待中的任务。
最终,所有任务都完成执行。
3.2 使用 Callable 和 Future
如果需要每个任务执行之后需要返回一个结果,可以实现Callable接口并重写call方法
- 使用 Callable 接口和 Future 对象可以提交有返回值的任务,并获取任务执行结果。
- ExecutorService 提供了管理线程池和并发任务执行的能力。
- submit 方法用于提交 Callable 任务,Future 对象用于获取任务的结果或状态。
通过 future.get() 方法可以阻塞等待任务执行完成并获取结果。
import java.time.LocalDateTime; import java.util.ArrayList; import java.util.concurrent.*; public class CallableExample { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(2); ArrayList<Future<String>> resultList = new ArrayList<>(); for (int i = 1; i <= 5; i++) { Future<String> submit = executorService.submit(new CallableTask(i)); resultList.add(submit); } executorService.shutdown(); resultList.forEach(result -> { try { System.out.println(result.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }); } } class CallableTask implements Callable<String> { private final int taskId; CallableTask(int taskId) { this.taskId = taskId; } @Override public String call() throws Exception { String threadName = Thread.currentThread().getName(); System.out.println("第 "+taskId+" 个线程,线程名:" + threadName + " 开始时间:" + LocalDateTime.now()); System.out.println("系统需要业务操作1秒"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "第 "+taskId+" 个线程,线程名:" + threadName + " 结束时间:" + LocalDateTime.now(); } }
3.3 使用 ScheduledExecutorService 调度任务
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledExecutorServiceExample { public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); // 延迟5秒后执行任务 scheduledExecutorService.schedule(new Task(1), 5, TimeUnit.SECONDS); // 每3秒执行一次任务 scheduledExecutorService.scheduleAtFixedRate(new Task(2), 0, 3, TimeUnit.SECONDS); // 在上一个任务完成后3秒再执行 scheduledExecutorService.scheduleWithFixedDelay(new Task(3), 0, 3, TimeUnit.SECONDS); try { Thread.sleep(15000); // 等待一段时间 } catch (InterruptedException e) { e.printStackTrace(); } scheduledExecutorService.shutdown(); } } class Task implements Runnable { private final int taskId; Task(int taskId) { this.taskId = taskId; } @Override public void run() { System.out.println("第 " + taskId + " 线程, 线程名 " + Thread.currentThread().getName()); try { Thread.sleep(2000); // 模拟任务执行时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("第 " + taskId + " 线程, 线程名 " + Thread.currentThread().getName()+" 结束时间 "+ LocalDateTime.now()); } }
4. 更多高级用法
4.1 使用 invokeAll 和 invokeAny
- invokeAll:批量提交任务,并等待所有任务完成,返回所有任务的结果。
- invokeAny:批量提交任务,并返回最先完成任务的结果。
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
public class AdvancedExecutorServiceExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
List<Callable<String>> tasks = Arrays.asList(
new CallableTask(1),
new CallableTask(2),
new CallableTask(3)
);
try {
// 批量提交任务并获取所有任务的结果
List<Future<String>> futures = executorService.invokeAll(tasks);
for (Future<String> future : futures) {
System.out.println("线程结果: " + future.get());
}
// 批量提交任务并获取最先完成任务的结果
String result = executorService.invokeAny(tasks);
System.out.println("最先完成的是: " + result);
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
class CallableTask implements Callable<String> {
private final int taskId;
CallableTask(int taskId) {
this.taskId = taskId;
}
@Override
public String call() throws Exception {
String threadName = Thread.currentThread().getName();
System.out.println("第 "+taskId+" 个线程,线程名:" + threadName + " 开始时间:" + LocalDateTime.now());
System.out.println("系统需要业务操作1秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "第 "+taskId+" 个线程,线程名:" + threadName + " 结束时间:" + LocalDateTime.now();
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。