假设您想要执行一些任务。由于通过单个线程执行它可能需要相当长的时间才能获得结果,因此您决定使用可靠的 ExecutorService 通过多个线程处理它。
这是一个示例:
public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { int temp = i; executorService.submit(() -> { task(temp); }); } executorService.shutdown(); System.out.println("ExecutorService is shutdown"); } private static void task(int temp) { try { TimeUnit.SECONDS.sleep(1L); System.out.println("Task " + temp + " completed"); } catch (InterruptedException e) { throw new RuntimeException(e); } }
当然,像往常一样,如果不使用“睡眠”作为任务执行的原型,任何线程示例都是不完整的。
它输出,
ExecutorService is shutdown Task 1 completed Task 2 completed Task 0 completed Task 4 completed Task 3 completed
现在想象一下有一个无穷无尽的任务队列,而你不知道其中的数量。也许它们是由数据库中动态添加的条目数量决定的。
例如,一家银行,它必须全天处理大量交易。交易结束时间为下午5点,超过该时间将不再接受任何额外任务。
但是,您确实知道任务的数量是有限的,并且将在某个时间点结束。
如何知道所有任务完成的时间点?
如果您注意到上面的代码片段,ExecutorService.shutdown() 会使主线程立即退出,但后台线程仍然处理已接受的任务直至完成。有什么方法可以让您收到完成通知吗?
我想到了几个解决方案:
有更好的方法来解决这个问题吗?
Java 确实提供了一种更好且相对未知的方法来解决这个问题。这里的“技巧”是要知道 Executors.newFixedThreadPool 本质上是一个具有预定义值的 ThreadPoolExecutor。我们来看看 Executors.newFixedThreadPool 的实现。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
我真的建议阅读这里的 ThreadPoolExecutor 文档。 ExecutorService 是 ThreadPoolExecutor 的便捷包装。
...强烈建议程序员使用更方便的Executors工厂方法Executors.newCachedThreadPool()(无界线程池,具有自动线程回收功能)、Executors.newFixedThreadPool(int)(固定大小线程池)和Executors.newSingleThreadExecutor( )(单后台线程),为最常见的使用场景预配置设置。
将帮助我们解决问题的部分是:
钩子方法
此类提供了受保护的可重写 beforeExecute(Thread, Runnable) 和 afterExecute(Runnable, Throwable) 方法,这些方法在每个任务执行之前和之后调用。这些可用于操纵执行环境;例如,重新初始化 ThreadLocals、收集统计信息或添加日志条目。此外,可以重写方法终止()以执行执行器完全终止后需要完成的任何特殊处理。
我们可以使用终止方法来通知我们同样的事情!但我们该如何使用它呢?
public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { int temp = i; executorService.submit(() -> { task(temp); }); } executorService.shutdown(); System.out.println("ExecutorService is shutdown"); } private static void task(int temp) { try { TimeUnit.SECONDS.sleep(1L); System.out.println("Task " + temp + " completed"); } catch (InterruptedException e) { throw new RuntimeException(e); } }
如果您不喜欢匿名类(像我一样),您始终可以自己扩展 ThreadPoolExecutor 来创建自定义类。
ExecutorService is shutdown Task 1 completed Task 2 completed Task 0 completed Task 4 completed Task 3 completed
这是验证它是否按照我们的预期工作的输出。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
您还使用了哪些其他相对未知的片段?请在评论中告诉我!
以上是掌握 ExecutorService 关闭:跟踪线程池终止的详细内容。更多信息请关注PHP中文网其他相关文章!