线程池是一种管理和复用线程的机制,它包含一组预先创建的线程,用于执行各种任务。线程池的主要作用是提高多线程应用程序的性能和效率,并提供对线程的生命周期和资源的管理,包括线程的创建、销毁和复用。主要作用如下:
线程池相关类关系图
使用线程池,最简单的方法就是使用 Executors 类。Executors 有以下核心方法
Executors 使用方法
private static void testExecutors() throws ExecutionException, InterruptedException {
// 创建一个线程池,核心线程数为2(默认运行的线程数量为2)
ExecutorService executorService = Executors.newFixedThreadPool(2);
//ExecutorService executorService = Executors.newCachedThreadPool();
//ExecutorService executorService = Executors.newSingleThreadExecutor();
// 方法一:使用线程池执行任务,没有返回值
executorService.execute(new Runnable() {
@Override
public void run() {
someTask();
}
});
// 方法二:使用线程池提交任务,有返回值
Future<Boolean> future = executorService.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
someTask();
return true;
}
});
// 方法三:执行任务多个,有返回值
List<Callable<Boolean>> callables = new ArrayList<>();
callables.add(()->{
// 这是一个 Lambda 表达式任务
return true;
});
List<Future<Boolean>> futures = executorService.invokeAll(callables);
// 等待任务执行完成
executorService.shutdown();
}
private static void someTask() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("这里是一个复杂的业务逻辑(耗时任务)");
}
下面是使用 Executors 创建线程池并执行线程的示例。
下面创建一个线程池,包括2个核心线程,执行一个 Lambda 表达式定义的任务
private static void test1() throws ExecutionException, InterruptedException {
// 创建一个线程池,核心线程数为2(默认运行的线程数量为2)
ExecutorService executorService = Executors.newFixedThreadPool(2);
// submit 是异步的
Future<Integer> future = executorService.submit(() -> {
// 这里是一个复杂的业务逻辑(耗时任务)
return 1;
});
// get 是同步的,阻塞的
int result = future.get();
System.out.println("result="+result);
executorService.shutdown();
}
为了方便理解,我将上面的 Lambda 表达式任务改成 Callable 接口任务,如下:
private static void test2() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Callable callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// 这里是一个复杂的业务逻辑(耗时任务)
return 2;
}
};
// submit 是异步的
Future<Integer> future = executorService.submit(callable);
// get 是同步的,阻塞的
int result = future.get();
System.out.println("result="+result);
executorService.shutdown();
}
从上面线程池的例子中可以看出当向线程池中提交任务(submit)时返回了一个 Future 对象。
FutureTask是Java中的一个实现了Future和Runnable接口的类,用于表示一个异步计算任务。用于获取计算结果、取消任务、判断任务是否完成等功能。主要方法如下:
下面使用FutureTask包装 Lambda 任务,然后新创建一个线程来执行,再使用 FutureTask 的 get 方法获取返回值。
private static void test3() throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
// 这里是一个复杂的业务逻辑(耗时任务)
return 3;
});
new Thread(futureTask).start();
System.out.println("result="+futureTask.get());
}
Future 还有一个很好用的子类叫 CompletableFuture 。他简化了 Future 类型任务执行,主要功能如下:
CompletableFuture 使用示例
private static void test4() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
// 这里是一个复杂的业务逻辑(耗时任务)
return 1;
});
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
// 这里是一个复杂的业务逻辑(耗时任务)
return 2;
});
CompletableFuture<Integer> completableFuture3 = CompletableFuture.supplyAsync(() -> {
// 这里是一个复杂的业务逻辑(耗时任务)
return 3;
});
// 等待所有任务完成
CompletableFuture.allOf(completableFuture1, completableFuture2, completableFuture3);
System.out.println("r1="+completableFuture1.get()+",r2="+completableFuture2.get()+",r3="+completableFuture3.get());
// 链式调用
CompletableFuture completableFuture4 = CompletableFuture.supplyAsync(() -> {
// 这里是一个复杂的业务逻辑(耗时任务)
return 4;
}).thenApply(String::valueOf).thenApply(text->"hello "+ text).thenAccept(System.out::println);
}
使用 Executors.newScheduledThreadPool 可以创建定时任务线程池,定时任务线池可以使任务在某个时间点上执行.
如下,创建的线程池执行任务时,任务将在30分钟以后执行。
private static void test5() throws ExecutionException, InterruptedException {
// 如下,每隔30分钟执行一次任务
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
scheduledExecutorService.scheduleWithFixedDelay(() -> {
// 这里是一个复杂的业务逻辑(耗时任务)
}, 0, 30, TimeUnit.MINUTES); // 30 表示延后30分钟
// 等待任务执行
scheduledExecutorService.shutdown();
}
任务分叉合并线程池内部使用 ForkJoinPool 实现,并且每个线程都有自己的工作队列。当某个线程的队列为空时,它可以从其他线程的队列中“窃取”任务来执行。
任务分叉合并线程池 ForkJoinPool 可以执以下两种任务
RecursiveAction 不带返回值的递归任务
RecursiveTask 带返回值的递归任务
RecursiveAction action = new RecursiveAction() { @Override protected void compute() { // 复杂计算任务 } }; RecursiveTask task = new RecursiveTask() { @Override protected Object compute() { // 复杂计算任务 return null; } }; ForkJoinPool p = new ForkJoinPool(); p.execute(action); p.execute(task);
这种线程池适用于执行大量的独立、耗时较长的任务,可以更好地利用多核处理器的性能。
请注意,工作窃取线程池在不同的操作系统和Java版本上可能会有一些差异,因此在具体的应用场景中,你可能需要根据性能和需求进行调优。
private static void test6() throws ExecutionException, InterruptedException {
// 分叉任务线程池
ExecutorService executorService = Executors.newWorkStealingPool();
executorService.execute(() -> {
// 这里是一个复杂的业务逻辑(耗时任务)
});
// 等待任务执行
executorService.shutdown();
}