通常线程池不仅仅只是这么简单的管理线程,从这个角度出发,线程池可以用来集中管理一系列任务
invokeAny / All
invokeAny
当其中一个线程结束了,别的也就都停止了。可以用在搜索答案
invokeAll
一定要全部线程都结束了才停止,并且返回这些线程的返回值
List<Callable<T>> tasks = . . .;
List<Future<T>> results = executor.invokeAll(tasks);
for (Future<T> result : results)
processFurther(result.get()); // 顺序遍历,不好
/* ------------------ */
var service = new ExecutorCompletionService<T>(executor);
for (Callable<T> task : tasks)
service.submit(task);
for (int i = 0; i < tasks.size(); i++)
processFurther(service.take().get());
前面顺序遍历不好的地方在于,他是顺序的,而线程不一定按原本顺序执行完,如果前面没好,后面好了,这样前面就要等后面,浪费时间
所以考虑使用 ExecutorCompletionService
包一下原来的线程池,对新的线程池提交任务。并且注意到这个新的线程池并没有类似于 invokeAll
这样的操作,只能用循环来提交
实际上,ExecutorCompletionService
内部维护了一个 blocking queue,你看有个 take
方法吧
对比一下
用线程池的方法安排,改变了写任务的思路
对比原来的写法,会有一个循环控制线程数量,每个循环就会新建一个线程,同时每个线程里头会有一个循环,不断的执行任务。说实话,这样的写法有些“面向过程”了
而线程池的想法则是有些“面向对象”,我只需要写一系列任务,这些任务可以重复,可以很多很多,不需要任何循环,循环的事情是线程池做的
线程池不会因为任务多,线程也多,他会按照既定的方式安排线程的数量,多个任务就如此巧妙的分配给了不同的线程