赞
踩
当我们想使用多线程的方法去执行一些逻辑,并想要获取执行的结果的时候。
我们会创建一个线程池,然后使用submit方法提交任务。然后通过get方法去获取执行的结果。
<T> Future<T> submit(Callable<T> task);
如果,想要所有的任务执行完毕后,主线程才继续往下执行。我们一般的做法是
public class ExTest { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); List<Future<XxxTask.Result>> futures = new ArrayList<>(); futures.add(executorService.submit(new XxxTask(4000))); futures.add(executorService.submit(new XxxTask(3000))); futures.add(executorService.submit(new XxxTask(8000))); futures.add(executorService.submit(new XxxTask(1000))); long startAll = System.currentTimeMillis(); executorService.shutdown(); try { executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } futures.forEach(x -> { try { long start = System.currentTimeMillis(); XxxTask.Result result = x.get(); long end = System.currentTimeMillis(); System.out.println("当前线程" + Thread.currentThread() + "消耗了" + (end - start) / 1000); System.out.println(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); long endAll = System.currentTimeMillis(); System.out.println("一共消耗了" + (endAll - startAll) / 1000 + "s"); } } class XxxTask implements Callable<XxxTask.Result> { public XxxTask(Integer time) { this.time = time; } private Integer time; @Override public Result call() throws Exception { Thread.sleep(time); // do something return new Result(); } public static class Result { public String message; } }
将线程池 shutdown,就是平滑的停止(该方法会使线程池不再接受新的任务,已经接受的任务会继续执行完毕)。
awaitTermination方法需要和shutdown方法配合。其目的是使主线程堵塞,直到线程池任务执行完毕。
执行结果如下
当前线程Thread[main,5,main]消耗了0
当前线程Thread[main,5,main]消耗了0
当前线程Thread[main,5,main]消耗了0
当前线程Thread[main,5,main]消耗了0
一共消耗了8s
之所以消耗都是0s,是因为在上面任务已经执行完了,调用get方法不会再消耗时间。
但是,如果上述代码是在一个接口中,对方会多次调用这个接口。这样写会产生的后果是,线程池频繁的创建以及销毁。会浪费很多资源。
因此 上述的代码尽量用来线程池创建的次数少的地方。比如发布项目时的数据初始化。只创建一个线程池。然后多个线程跑。而在接口中建议使用下面的方法。
此get方法会获取任务所执行的结果。如果那个子线程没有执行完任务,主线程会一直堵塞直到获取到结果。
利用这个特点,我们要想主线程等待所有的子线程执行完毕,只要遍历Future,分别get即可。
代码如下所示
public class ExTest { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); List<Future<XxxTask.Result>> futures = new ArrayList<>(); futures.add(executorService.submit(new XxxTask(4000))); futures.add(executorService.submit(new XxxTask(3000))); futures.add(executorService.submit(new XxxTask(8000))); futures.add(executorService.submit(new XxxTask(1000))); long startAll = System.currentTimeMillis(); futures.forEach(x -> { try { long start = System.currentTimeMillis(); XxxTask.Result result = x.get(); long end = System.currentTimeMillis(); System.out.println("当前线程" + Thread.currentThread() + "消耗了" + (end - start) / 1000); System.out.println(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); long endAll = System.currentTimeMillis(); System.out.println("一共消耗了" + (endAll - startAll) / 1000 + "s"); } } class XxxTask implements Callable<XxxTask.Result> { public XxxTask(Integer time) { this.time = time; } private Integer time; @Override public Result call() throws Exception { Thread.sleep(time); // do something return new Result(); } public static class Result { } }
上述代码。简单的创建了一个线程池(暂不考虑OOM)。当在写业务代码的时候,将其作为一个成员变量,使用完之后,不要销毁即可。就可以实现线程池的重复使用。
并且可以同步的走完这个代码。让主线程等待子线程执行完毕之后再往下执行。
下面是执行的结果
当前线程Thread[main,5,main]消耗了3
当前线程Thread[main,5,main]消耗了0
当前线程Thread[main,5,main]消耗了3
当前线程Thread[main,5,main]消耗了0
一共消耗了8s
其中主线程在等待第一个任务时;第二,第三,第四的任务也已经开始执行了。同样是并行执行的,效率也不慢。
但是,千万要小心的是,千万不要这样写!!!
ExecutorService executorService = Executors.newFixedThreadPool(5);
long startAll = System.currentTimeMillis();
Future<XxxTask.Result> submit = executorService.submit(new XxxTask(4000));
submit.get();
Future<XxxTask.Result> submit2 = executorService.submit(new XxxTask(3000));
submit2.get();
Future<XxxTask.Result> submit3 = executorService.submit(new XxxTask(8000));
submit3.get();
Future<XxxTask.Result> submit1 = executorService.submit(new XxxTask(1000));
submit1.get();
long endAll = System.currentTimeMillis();
System.out.println("一共消耗了" + (endAll - startAll) / 1000 + "s");
这样会把多线程写成单线程的执行,切记切记。
有关闭锁的知识,请看这篇文章 -->
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。