在默認(rèn)的情況下,線程Thread對(duì)象不具有返回值的功能,如果在需要取得返回值的情況下是極為不方便的,但在Java1.5的并發(fā)包中可以使用Future和Callable來(lái)使線程具有返回值的功能。
1.Future和Callable的介紹
接口Callable與線程功能密不可分,但和Runnable的主要區(qū)別為:
(1) 接口Callable的call()方法可以有返回值,但Runnable接口的run()方法沒(méi)有返回值。
(2) Callable接口的call()方法可以聲明拋出異常,而Runnable接口的run()方法不可以聲明拋出異常。
執(zhí)行完Callable接口中的任務(wù)后,返回值是通過(guò)Future接口進(jìn)行獲得的。
2.方法get()結(jié)合ExecutorService中的submit(Callable<T>)的使用
方法submit(Callable<T>)可以執(zhí)行參數(shù)為Callable的任務(wù),方法get()用于獲得返回值,示例如下:
import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { public MyCallable(int age) { public String call() throws Exception {
import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import mycallable.MyCallable; public static void main(String[] args) throws InterruptedException { MyCallable callable = new MyCallable(100); ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5L, TimeUnit.SECONDS, new LinkedBlockingDeque()); Future<String> future = executor.submit(callable); System.out.println("main A " + System.currentTimeMillis()); System.out.println(future.get()); System.out.println("main B " + System.currentTimeMillis()); } catch (ExecutionException e) {
3.方法get()結(jié)合ExecutorService中的submit(Runnable)和isDone()的使用
方法submit()不僅可以傳入Callable對(duì)象,也可以傳入Runnable對(duì)象,說(shuō)明submit方法支持有返回值和無(wú)返回值的功能。如果submit方法傳入Callable接口則可以有返回值,如果傳入Runnable則無(wú)返回值,打印的結(jié)果就是null。方法get()具有阻塞特性,而isDone()方法無(wú)阻塞特性。
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; * 如果submit方法傳入Callable接口則可以有返回值,如果傳入Runnable則無(wú)返回值,打印的結(jié)果就是null。方法get()具有阻塞特性,而isDone()方法無(wú)阻塞特性 public static void main(String[] args) { Runnable runnable = new Runnable() { System.out.println("打印的信息"); ExecutorService executorRef = Executors.newCachedThreadPool(); Future future = executorRef.submit(runnable); System.out.println(future.get() + " " + future.isDone()); } catch (InterruptedException e) { } catch (ExecutionException e) {
4.使用ExecutorService接口中的方法submit(Runnable,T result)
方法submit(Runnable,T result)的第2個(gè)參數(shù)result可以作為執(zhí)行結(jié)果的返回值,而不需要使用get()方法來(lái)進(jìn)行獲得。
public Userinfo(String username, String password) { this.username = username; this.password = password; public String getUsername() { public void setUsername(String username) { this.username = username; public String getPassword() { public void setPassword(String password) { this.password = password;
public class MyRunnable implements Runnable { private Userinfo userinfo; public MyRunnable(Userinfo userinfo) { this.userinfo = userinfo; userinfo.setUsername("usernameValue"); userinfo.setPassword("passwordValue");
import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import myrunnable.MyRunnable; FutureTask abc; //接口Future的實(shí)現(xiàn)類 public static void main(String[] args) { Userinfo userinfo = new Userinfo(); MyRunnable myrunnable = new MyRunnable(userinfo); ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()); Future<Userinfo> future = pool.submit(myrunnable, userinfo); System.out.println(future); System.out.println("begin time=" + System.currentTimeMillis()); System.out.println("get value " + userinfo.getUsername() + " " + userinfo.getPassword()); System.out.println(" end time=" + System.currentTimeMillis()); } catch (InterruptedException e) { } catch (ExecutionException e) {
5.方法cancel(boolean mayInterruptIfRunning) 和isCancelled()的使用
方法cancel(boolean mayInterruptIfRunning)的參數(shù)mayInterruptIfRunning的作用是:如果線程正在運(yùn)行則是否中斷正在運(yùn)行的線程,在代碼中需要使用if(Thread.currentThread().isInterrupted())進(jìn)行配合。
方法cancel()的返回值代表發(fā)送取消任務(wù)的命令是否成功完成。
6.方法get(long timeout,TimeUnit unit)的使用
方法get(long timeout,TimeUnit unit)的作用是在指定的最大時(shí)間內(nèi)等待獲得返回值。
import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { public String call() throws Exception { Thread.sleep(10000); //目的是為了在指定時(shí)間內(nèi)獲取不到返回值 System.out.println("sleep 10秒執(zhí)行完了!");
import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import mycallable.MyCallable; public static void main(String[] args) { MyCallable callable = new MyCallable(); ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5L, TimeUnit.SECONDS, new LinkedBlockingDeque()); System.out.println("begin " + System.currentTimeMillis()); Future<String> future = executor.submit(callable); System.out.println("返回值" + future.get(5, TimeUnit.SECONDS)); System.out.println(" end " + System.currentTimeMillis()); } catch (InterruptedException e) { System.out.println("進(jìn)入catch InterruptedException"); } catch (ExecutionException e) { System.out.println("進(jìn)入catch ExecutionException"); } catch (TimeoutException e) { System.out.println("進(jìn)入catch TimeoutException");
7.自定義拒絕策略RejectedExecutionHandler接口的使用
接口RejectedExecutionHandler的主要作用是當(dāng)線程池關(guān)閉后依然有任務(wù)要執(zhí)行時(shí),可以實(shí)現(xiàn)一些處理。
package com.executionhandler; import java.util.concurrent.FutureTask; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; public class MyRejectedExecutionHandler implements RejectedExecutionHandler { public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println(((FutureTask) r).toString() + " 被拒絕!");
package com.executionhandler; public class MyRunnable implements Runnable { public MyRunnable(String username) { this.username = username; System.out.println(username + " 在運(yùn)行!");
package com.executionhandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) service; executor.setRejectedExecutionHandler(new MyRejectedExecutionHandler()); service.submit(new MyRunnable("A")); service.submit(new MyRunnable("B")); service.submit(new MyRunnable("C")); service.submit(new MyRunnable("D"));
8.方法execute()與submit()的區(qū)別
(1) 方法execute()沒(méi)有返回值,而submit()方法可以有返回值。
(2) 方法execute()在默認(rèn)的情況下異常直接拋出,不能捕獲,但可以通過(guò)自定義ThreadFactory的方式進(jìn)行捕獲,而submit()方法在默認(rèn)的情況下,可以catch Execution-Exeception捕獲異常
9.驗(yàn)證Future的缺點(diǎn)

import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { public MyCallable(String username, long sleepValue) { this.username = username; this.sleepValue = sleepValue; public String call() throws Exception { System.out.println(username); Thread.sleep(sleepValue); return "return " + username;
import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import mycallable.MyCallable; public static void main(String[] args) { MyCallable callable1 = new MyCallable("username1", 5000); MyCallable callable2 = new MyCallable("username2", 4000); MyCallable callable3 = new MyCallable("username3", 3000); MyCallable callable4 = new MyCallable("username4", 2000); MyCallable callable5 = new MyCallable("username5", 1000); List<Callable> callableList = new ArrayList<Callable>(); callableList.add(callable1); callableList.add(callable2); callableList.add(callable3); callableList.add(callable4); callableList.add(callable5); List<Future> futureList = new ArrayList<Future>(); ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()); for (int i = 0; i < 5; i++) { futureList.add(executor.submit(callableList.get(i))); System.out.println("run first time= " + System.currentTimeMillis()); for (int i = 0; i < 5; i++) { System.out.println(futureList.get(i).get() + " " + System.currentTimeMillis()); // 說(shuō)明一個(gè)Future對(duì)應(yīng)指定的一個(gè)Callable } catch (InterruptedException e) { } catch (ExecutionException e) {
總結(jié):Future和Callable這兩個(gè)接口的優(yōu)點(diǎn)就是從線程中返回?cái)?shù)據(jù)以便進(jìn)行后期的處理,但是FutureTask類也有其自身的缺點(diǎn),就是阻塞性。
|