文章目录
问题一:需要计算班级所有成绩总和,使用100个线程,每个线程计算一个学生成绩;
问题二:需要破解一个文件的密码,使用100个线程破解;
1、获取所有线程执行结果
对于问题一,需要获取所有线程执行结果,将每个线程的结果相加。
使用线程池如下:
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 60L,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
1.1、使用线程池的invokeAll()
List<Future> invokeAl 1 (Col1ection<Cal 1 able > tasks )
传入任务集合,返回Future
集合,遍历future.get()
即可。
代码如下:
List<Callable<String>> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int index = i;
list.add(() -> {
try {
Random random = new Random();
int times = random.nextInt(10);
//模拟两次异常
if (index % 7 == 1) {
int tmp = index / 0;
}
TimeUnit.SECONDS.sleep(times);
return "子线程" + Thread.currentThread().getName() + " 耗费时长(s):" + times;
} catch (Exception e) {
return "子线程:"+ Thread.currentThread().getName() + ": " + e.getMessage();
}
});
}
List<Future<String>> futures = executor.invokeAll(list);
for (Future<String> future : futures) {
System.out.println(future.get());
}
结果如下:
子线程pool-1-thread-1 耗费时长(s):8
子线程:pool-1-thread-2: / by zero
子线程pool-1-thread-3 耗费时长(s):9
子线程pool-1-thread-4 耗费时长(s):4
子线程pool-1-thread-5 耗费时长(s):2
子线程pool-1-thread-6 耗费时长(s):2
子线程pool-1-thread-7 耗费时长(s):0
子线程pool-1-thread-8 耗费时长(s):8
子线程:pool-1-thread-9: / by zero
子线程pool-1-thread-10 耗费时长(s):6
主线程end-----
花费时间:9097
弊端
:需要等待所有线程全部执行完成才能获取到结果。
适用于任务之间有关联,或必须等待最终结果,或者任务需要一次性提交运行的情况。
1.2 直接使用线程池的submit()
Future submit(Callable task)
每次传入一个任务,返回future
执行结果。最后遍历future.get()
代码如下:
List<Future<String>> futureList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int index = i;
Future<String> future = executor.submit(() -> {
try {
Random random = new Random();
int times = random.nextInt(10);
//模拟两次异常
if (index % 7 == 1) {
int tmp = index / 0;
}
TimeUnit.SECONDS.sleep(times);
return "子线程" + Thread.currentThread().getName() + " 耗费时长(s):" + times;
} catch (Exception e) {
return "子线程:" + Thread.currentThread().getName() + ": " + e.getMessage();
}
});
futureList.add(future);
}
for (Future future : futureList) {
System.out.println(future.get());
}
结果如下:
子线程pool-1-thread-1 耗费时长(s):8
子线程:pool-1-thread-2: / by zero
子线程pool-1-thread-3 耗费时长(s):6
子线程pool-1-thread-4 耗费时长(s):4
子线程pool-1-thread-5 耗费时长(s):5
子线程pool-1-thread-6 耗费时长(s):4
子线程pool-1-thread-7 耗费时长(s):0
子线程pool-1-thread-8 耗费时长(s):4
子线程:pool-1-thread-9: / by zero
子线程pool-1-thread-10 耗费时长(s):7
主线程end-----
花费时间:8090
弊端
:从头遍历Futures,当第一个线程执行时间很长时,后续线程执行结果也无法拿到。
适用于不需要批量提交的任务,可以一个一个提交
1.3 使用ExecutorCompletionService
ExecutorCompletionService service = new ExecutorCompletionServiceo(executor)
ExecutorCompletionService
构建一个计算完成的future
队列
- Future< V > submit( Runnable task , V result ):提交一个任务给底层的执行器。
- Future< V > take( ): 移除一个已完成的结果, 如果没有任何已完成的结果可用则阻塞。
使用submit提交任务,每当任务执行完毕将结果放入 future队列,使用take获取结果,实现实时获取结果。
代码如下:
ExecutorCompletionService<String> executorCompletionService = new ExecutorCompletionService<>(executor);
for (int i = 0; i < 10; i++) {
int index = i;
executorCompletionService.submit(() -> {
try {
Random random = new Random();
int times = random.nextInt(10);
//模拟两次异常
if (index % 7 == 1) {
int tmp = index / 0;
}
TimeUnit.SECONDS.sleep(times);
return "子线程" + Thread.currentThread().getName() + " 耗费时长(s):" + times;
} catch (Exception e) {
return "子线程:" + Thread.currentThread().getName() + ": " + e.getMessage();
}
});
}
for (int i = 0; i < 10; i++) {
System.out.println(executorCompletionService.take().get());
}
结果如下:
子线程:pool-1-thread-2: / by zero
子线程:pool-1-thread-9: / by zero
子线程pool-1-thread-3 耗费时长(s):1
子线程pool-1-thread-7 耗费时长(s):2
子线程pool-1-thread-1 耗费时长(s):2
子线程pool-1-thread-6 耗费时长(s):5
子线程pool-1-thread-10 耗费时长(s):5
子线程pool-1-thread-4 耗费时长(s):6
子线程pool-1-thread-5 耗费时长(s):7
子线程pool-1-thread-8 耗费时长(s):9
主线程end-----
花费时间:9094
从结果可以看到获取到的线程结果不是按照任务的添加顺序,而是按照执行时间长短
适用于需要实时知道结果的情况。
2、获取多个线程的一个结果
对于问题二:需要破解一个文件的密码,使用100个线程破解;只需要获取最早的一个线程的执行结果即可,结算便可停止。
使用线程池如下:
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 60L,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
使用线程池的invokeAny()
T invokeAny(Collection<Callable> tasks )
传入任务集合,返回其中一个执行结果
与invokeAll
的区别在于:
invokeAll
:返回所有执行结果future集合
invokeAny
:返回其中一个结果
代码如下:
List<Callable<String>> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int index = i;
list.add(() -> {
try {
Random random = new Random();
int times = random.nextInt(10) +2;
TimeUnit.SECONDS.sleep(times);
return "子线程" + Thread.currentThread().getName() + " 耗费时长(s):" + times;
} catch (Exception e) {
return "子线程:"+ Thread.currentThread().getName() + ": " + e.getMessage();
}
});
}
System.out.println(executor.invokeAny(list));
结果如下:
子线程pool-1-thread-4 耗费时长(s):2
主线程end-----
花费时间:2106
如上结果所示,获取最早计算完成的结果。