CompletionService
接口CompletionService的功能是以異步的方式生產新的任務,一邊處理已完成任務的結果,這樣可以將執行任務與處理任務分離開來進行處理。使用submit執行任務,使用take取得已完成的任務,並按照完成這些任務的時間順序處理它們的結果。
接口 Completion Service的結構比較簡潔,僅有一個實現類 Executor Completion Service,該類的構造方法如圖所示。
從構造方法的聲明中可以發現,類 Executor Completion Service需要依賴於 Executor對象,大部分的實現也就是使用線程池 ThreadPoolExecutor對象。
take()方法
public class TestDemo {
public static void main(String[] args) {
try {
//take:獲取並移除下一個已完成任務的Futrue,如果目前不存在這樣的任務,則阻塞。
ExecutorService executorService = Executors.newCachedThreadPool();
CompletionService cs = new ExecutorCompletionService(executorService);
for (int i = 0; i < 10; i++) {
cs.submit(new Callable<String>() {
@Override
public String call() throws Exception {
long sleepValue = (int)(Math.random() * 1000);
System.out.println("sleep=" + sleepValue + " " + Thread.currentThread().getName());
Thread.sleep(sleepValue);
return "返回值:" + sleepValue + " " + Thread.currentThread().getName();
}
});
}
for (int i = 0; i < 10; i++) {
System.out.println(cs.take().get());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
從運行結果來看,方法 take()是按任務執行的速度,從快到慢的順序獲得Future對象,因爲打印的時間是從小到大。
sleep=738 pool-1-thread-1
sleep=911 pool-1-thread-8
sleep=467 pool-1-thread-10
sleep=407 pool-1-thread-7
sleep=131 pool-1-thread-3
sleep=79 pool-1-thread-6
sleep=563 pool-1-thread-4
sleep=869 pool-1-thread-5
sleep=545 pool-1-thread-2
sleep=551 pool-1-thread-9
返回值:79 pool-1-thread-6
返回值:131 pool-1-thread-3
返回值:407 pool-1-thread-7
返回值:467 pool-1-thread-10
返回值:545 pool-1-thread-2
返回值:551 pool-1-thread-9
返回值:563 pool-1-thread-4
返回值:738 pool-1-thread-1
返回值:869 pool-1-thread-5
返回值:911 pool-1-thread-8
poll()方法
方法poll()的作用是獲取並移除表示下一個已完成任務的 Future,如果不存在這樣的任務,則返回null,方法pol()無阻
塞的效果:
public class TestDemo2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
CompletionService cs = new ExecutorCompletionService(executorService);
for (int i = 0; i < 1; i++) {
cs.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(3000);
System.out.println("3秒過後");
return "返回值";
}
});
}
for (int i = 0; i < 1; i++) {
System.out.println(cs.poll());
}
}
}
poll()方法返回null,因爲當前沒有任何已完成任務的Future對象,所以返回爲null。poll()方法不具有阻塞特性。
poll(long timeout, TimeUnit unit)
方法Future pol(long timeout, TimeUnit unit)的作用是等待指定的timeout時間,在timeout時間之內獲取到值時立即向下繼續執行,如果超時也立即向下執行。
public class TestDemo3 {
public static void main(String[] args) {
try {
MyCallableA a = new MyCallableA();
MyCallableB b = new MyCallableB();
Executor executor = Executors.newSingleThreadExecutor();
CompletionService cs = new ExecutorCompletionService(executor);
cs.submit(a);
cs.submit(b);
for (int i = 0; i < 2; i++) {
System.out.println("zzzzzzzzzzz" + " " + cs.take());
}
System.out.println("main end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyCallableA implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("MyCallableA begin " + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("MyCallableA end " + System.currentTimeMillis());
return "returnA";
}
}
class MyCallableB implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("MyCallableB begin " + System.currentTimeMillis());
Thread.sleep(5000);
int i = 0;
if (i == 0) {
throw new Exception("拋出異常!");
}
System.out.println("MyCallableB end " + System.currentTimeMillis());
return "returnB";
}
}
MyCallableB雖然出現異常,但是並沒有調用FutureTask的get()方法,所以不出現異常。
調用get()方法:
調換A和B的執行順序:
任務B出現異常,任務A並未輸出。
調用poll()方法:
public class TestDemo3 {
public static void main(String[] args) {
try {
MyCallableA a = new MyCallableA();
MyCallableB b = new MyCallableB();
Executor executor = Executors.newSingleThreadExecutor();
CompletionService cs = new ExecutorCompletionService(executor);
cs.submit(a);
cs.submit(b);
for (int i = 0; i < 2; i++) {
System.out.println("zzzzzzzzzzz" + " " + cs.poll());
}
Thread.sleep(6000);
System.out.println("A處" + " " + cs.poll());
System.out.println("B處" + " " + cs.poll());
System.out.println("main end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyCallableA implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("MyCallableA begin " + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("MyCallableA end " + System.currentTimeMillis());
return "returnA";
}
}
class MyCallableB implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("MyCallableB begin " + System.currentTimeMillis());
Thread.sleep(5000);
int i = 0;
if (i == 0) {
throw new Exception("拋出異常!");
}
System.out.println("MyCallableB end " + System.currentTimeMillis());
return "returnB";
}
}
任務B出現異常後,poll()返回值爲null
poll()後調用get()方法獲取返回值:
A任務返回值正常,任務B返回值出現異常。
調換A和B任務執行順序:
任務A並未打印,任務B拋出異常。
Future submit(Runnable task, V result)
public class TestDemo4 {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
MyRunnable5 myRunnable5 = new MyRunnable5(userInfo);
Executor executor = Executors.newCachedThreadPool();
CompletionService cs = new ExecutorCompletionService(executor);
Future<UserInfo> future = cs.submit(myRunnable5, userInfo);
try {
System.out.println(future.get().getUsername() + " " + future.get().getPassword());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class UserInfo {
private String username;
private String password;
public UserInfo() {
super();
}
public UserInfo(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;
}
}
class MyRunnable5 implements Runnable {
private UserInfo userInfo;
public MyRunnable5(UserInfo userInfo) {
this.userInfo = userInfo;
}
@Override
public void run() {
userInfo.setUsername("usernameValue");
userInfo.setPassword("passwordValue");
System.out.println("run運行了 ");
}
}
總結
接口CompletionService完全可以避免FutureTask類阻塞的缺點,可更加有效地處理Future的返回值,也就是哪個任務先執行完,CompletionService 就先取得這個任務的返回值再處理。