原创转载请注明出处: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