原创转载请注明出处:http://agilestyle.iteye.com/blog/2343752
CompletionService的功能是以异步的方式一边生产新的任务,一边处理已完成任务的结果,这样可以将执行任务与处理任务分离开来进行处理。使用submit()执行任务,使用take()取得已完成的任务,并按照完成这些任务的时间顺序处理它们的结果。这样就可以很好的解决Future接口的get()方法的阻塞特性带来的性能问题。
Future的缺点
MyCallable.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { private String threadName; private Integer sleepTime; public MyCallable(String threadName, Integer sleepTime) { this.threadName = threadName; this.sleepTime = sleepTime; } @Override public String call() throws Exception { System.out.println(threadName); Thread.sleep(sleepTime); return "return " + threadName; } }
CompletionServiceTest0.java
package org.fool.java.concurrent.completionservice; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; public class CompletionServiceTest0 { public static void main(String[] args) { MyCallable thread1 = new MyCallable("thread1", 5000); MyCallable thread2 = new MyCallable("thread2", 4000); MyCallable thread3 = new MyCallable("thread3", 3000); MyCallable thread4 = new MyCallable("thread4", 2000); MyCallable thread5 = new MyCallable("thread5", 1000); List<Callable> callableList = new ArrayList<>(); callableList.add(thread1); callableList.add(thread2); callableList.add(thread3); callableList.add(thread4); callableList.add(thread5); ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); List<Future> futureList = new ArrayList<>(); callableList.forEach(callable -> futureList.add(executor.submit(callable))); futureList.forEach(future -> { try { System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); } }
Run
Note:
可以看到,线程1执行的时间耗时最多, 调用get()方法时候一直阻塞到此任务完成时为止,也就是主线程并不能保证首先获得的是最先完成任务的返回值,一旦获取不到,就进入排队等待,大大影响运行效率,这就是Future的缺点。
使用CompletionService
CompletionService可以解决Future阻塞同步性的问题。
CompletionServiceTest1.java
package org.fool.java.concurrent.completionservice; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; public class CompletionServiceTest1 { public static void main(String[] args) { MyCallable thread1 = new MyCallable("thread1", 5000); MyCallable thread2 = new MyCallable("thread2", 4000); MyCallable thread3 = new MyCallable("thread3", 3000); MyCallable thread4 = new MyCallable("thread4", 2000); MyCallable thread5 = new MyCallable("thread5", 1000); List<Callable> callableList = new ArrayList<>(); callableList.add(thread1); callableList.add(thread2); callableList.add(thread3); callableList.add(thread4); callableList.add(thread5); ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); CompletionService service = new ExecutorCompletionService(executor); callableList.forEach(callable -> service.submit(callable)); callableList.forEach(i -> { try { System.out.println(service.take().get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); } }
Run
Note:
CompletionService完全解决了Future阻塞的特性,哪个任务先执行完,哪个任务的返回值就先打印。
但是,在CompletionService接口中如果当前任务被执行完,则service.take().get()方法还是呈阻塞特性。
take()
take()取得最先完成任务的Future对象,谁执行时间最短谁最先返回。
CompletionServiceTest2.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.*; public class CompletionServiceTest2 { public static void main(String[] args) { try { ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); CompletionService service = new ExecutorCompletionService(executor); for (int i = 0; i < 10; i++) { service.submit(new Callable<String>() { @Override public String call() throws Exception { long sleepTime = (int) (Math.random() * 1000); System.out.println("sleep=" + sleepTime + " " + Thread.currentThread().getName()); Thread.sleep(sleepTime); return Thread.currentThread().getName() + " sleep " + sleepTime; } }); } for (int i = 0; i < 10; i++) { System.out.println(service.take().get()); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
Run
Note:
take()是按任务执行的速度,从快到慢的顺序获得Future对象
poll()
poll()的作用是获取并移除表示下一个已完成的Future,如果不存在这样的任务,则返回null,且无阻塞。
CompletionServiceTest3.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.*; public class CompletionServiceTest3 { public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); CompletionService service = new ExecutorCompletionService(executor); service.submit(new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(5000); System.out.println("after 5 seconds..."); return Thread.currentThread().getName(); } }); System.out.println(service.poll()); } }
Run
poll(long timeout, TimeUnit unit)
poll(long timeout, TimeUnit unit)作用是等待指定的timeout时间,在timeout时间之内获取到值时立即向下继续执行,如果超时也立即向下执行。
MyCallableA.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.Callable; public class MyCallableA implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(5000); System.out.println("MyCallableA " + System.currentTimeMillis()); return "CallableA"; } }
Note:
A睡5秒
MyCallableB.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.Callable; public class MyCallableB implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(10000); System.out.println("MyCallableB " + System.currentTimeMillis()); return "CallableB"; } }
Note:
B睡10秒
CompletionServiceTest4.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.*; public class CompletionServiceTest4 { public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); CompletionService service = new ExecutorCompletionService(executor); service.submit(new MyCallableA()); service.submit(new MyCallableB()); for (int i = 0; i < 2; i++) { System.out.println(service.poll()); } System.out.println("main end..."); } }
Run
Note:
返回2个null值,因为任务未执行完毕。
CompletionServiceTest5.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.*; public class CompletionServiceTest5 { public static void main(String[] args) { try { ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); CompletionService service = new ExecutorCompletionService(executor); service.submit(new MyCallableA()); service.submit(new MyCallableB()); for (int i = 0; i < 2; i++) { System.out.println(service.poll(7, TimeUnit.SECONDS).get()); } System.out.println("main end..."); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
Note:
设置timeout为7秒
Run
Note:
返回2个值,一共等待了14秒
CompletionServiceTest6.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.*; public class CompletionServiceTest6 { public static void main(String[] args) { try { ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); CompletionService service = new ExecutorCompletionService(executor); service.submit(new MyCallableA()); service.submit(new MyCallableB()); System.out.println("after 4 seconds..."); System.out.println(service.poll(4, TimeUnit.SECONDS)); System.out.println("after 6 seconds..."); System.out.println(service.poll(2, TimeUnit.SECONDS).get()); System.out.println("after 12 seconds..."); System.out.println(service.poll(6, TimeUnit.SECONDS).get()); System.out.println("main end..."); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
Run
Note:
第4秒的时候,返回null,因为还未达到5秒,其他2次都打印得到返回值,因为任务已经完成。
所以poll(long timeout, TimeUnit unit)中的timeout的值如果小于任务执行时间,则返回值为null
submit(Runnable task, V result)
CompletionServiceTest7.java
package org.fool.java.concurrent.completionservice; import java.util.concurrent.*; public class CompletionServiceTest7 { public static void main(String[] args) { try { User user = new User(); MyThread thread = new MyThread(user); ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); CompletionService service = new ExecutorCompletionService(executor); Future<User> future = service.submit(thread, user); System.out.println(future.get().getUsername() + ":" + future.get().getPassword()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } public static class User { private String username; private String password; public User() { } public User(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } public static class MyThread implements Runnable { private User user; public MyThread(User user) { this.user = user; } @Override public void run() { user.setUsername("hello"); user.setPassword("world"); System.out.println("my thread invoked..."); } } }
Run