频繁的创建和销毁线程需要花费大量时间,Java支持创建线程池,将多任务交给一组线程执行,提高执行效率。
Java标准库提供 ExecutorService 接口表示线程池,实现类有FixedThreadPool、CachedThreadPool、SingleThreadExecutor;创建线程池的方法被封装到Executors类。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPool { public static void main(String[] args) { ExecutorService es = Executors.newFixedThreadPool(6); for (int i = 0; i < 10; i++) { es.submit(new Task(i)); } es.shutdown(); } } class Task implements Runnable { private int count = 0; Task(int count) { this.count = count; } @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.count); } } // 2 // 4 // 1 // 5 // 3 // 0 // 8 // 6 // 9 // 7
由于FixedThreadPool 总是创建固定的线程数量,当一次性提交的任务数量超过线程池中线程的数时,多出的任务需要等待。总结:
线程池结束方式:
ScheduledThreadPool 重复执行任务的线程池,Java标准库使用 ScheduledExecutorService 表示接口,创建方法同样被封装到Executors中。
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ThreadPool { public static void main(String[] args) throws InterruptedException { ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); ses.schedule(new ScheduledTask(), 1, TimeUnit.SECONDS); ses.scheduleAtFixedRate(new ScheduledTask(), 0, 1, TimeUnit.SECONDS); //ses.awaitTermination(10, TimeUnit.SECONDS); Thread.sleep(10000); ses.shutdownNow(); } } class ScheduledTask implements Runnable { private int i = 0; @Override public void run() { System.out.println(i++); } }
其中:
使用线程池可以方便的处理多任务,只需要任务实现Runable接口,调用submit方法提交执行即可。但缺点是无法返回值,Java提供Callable接口返回执行结果。
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class ThreadPool { public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { ExecutorService executor = Executors.newFixedThreadPool(4); Callable<String> task = new Task(); Future<String> future = executor.submit(task); String res = future.get(1, TimeUnit.SECONDS); System.out.println(res); } }class Task implements Callable<String>{ public String call() throws InterruptedException { Thread.sleep(2000); //模拟耗时任务 return "done"; } }
可以看到 Callable接口是泛型接口,可以指定返回类型。需要注意 future对象:
使用Future获取异步执行的结果或阻塞当前线程,Java 8引入的CompletableFuture,支持异步回调,其用法类似JavaScript中的Promise对象的用法。
import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; public class ThreadPool { public static void main(String[] args) throws InterruptedException { // 异步执行 CompletableFuture<String> apple = CompletableFuture.supplyAsync(new FetchApple()); apple.thenAccept((result) -> { System.out.println(result); }); apple.exceptionally((e) -> { e.printStackTrace(); return null; }); Thread.sleep(3000); } } class FetchApple implements Supplier<String> { @Override public String get() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "Apple"; } }
CompletableFuture支持传入成功和失败的回调,在将来的时刻执行,并且不会阻塞当前线程。同时 CompletableFuture支持类似于Promise的链式回调,Promise.race的竞争并发执行和类似Promise.all的全部执行,但是用法上会复杂许多。
由于Java的严谨性,CompletableFuture.supplyAsync传入的对象需要实现Supplier<T>接口、成功的回调thenAccept传入对象需要实现Consumer<T>接口、失败的回调exceptionally需要传入实现Function<T,R>接口的对象。但好在支持的lambda语法可以进行简化。
参考链接:
https://www.liaoxuefeng.com/wiki/1252599548343744/1306581155184674