在默认的情况下,线程Thread对象不具有返回值的功能,如果在需要取得返回值的情况下是极为不方便的,但在Java1.5的并发包中可以使用Future和Callable来使线程具有返回值的功能。
1.Future和Callable的介绍
接口Callable与线程功能密不可分,但和Runnable的主要区别为:
(1) 接口Callable的call()方法可以有返回值,但Runnable接口的run()方法没有返回值。
(2) Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常。
执行完Callable接口中的任务后,返回值是通过Future接口进行获得的。
2.方法get()结合ExecutorService中的submit(Callable<T>)的使用
方法submit(Callable<T>)可以执行参数为Callable的任务,方法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()结合ExecutorService中的submit(Runnable)和isDone()的使用
方法submit()不仅可以传入Callable对象,也可以传入Runnable对象,说明submit方法支持有返回值和无返回值的功能。如果submit方法传入Callable接口则可以有返回值,如果传入Runnable则无返回值,打印的结果就是null。方法get()具有阻塞特性,而isDone()方法无阻塞特性。
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; * 如果submit方法传入Callable接口则可以有返回值,如果传入Runnable则无返回值,打印的结果就是null。方法get()具有阻塞特性,而isDone()方法无阻塞特性 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个参数result可以作为执行结果的返回值,而不需要使用get()方法来进行获得。
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的实现类 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)的参数mayInterruptIfRunning的作用是:如果线程正在运行则是否中断正在运行的线程,在代码中需要使用if(Thread.currentThread().isInterrupted())进行配合。
方法cancel()的返回值代表发送取消任务的命令是否成功完成。
6.方法get(long timeout,TimeUnit unit)的使用
方法get(long timeout,TimeUnit unit)的作用是在指定的最大时间内等待获得返回值。
import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { public String call() throws Exception { Thread.sleep(10000); //目的是为了在指定时间内获取不到返回值 System.out.println("sleep 10秒执行完了!");
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("进入catch InterruptedException"); } catch (ExecutionException e) { System.out.println("进入catch ExecutionException"); } catch (TimeoutException e) { System.out.println("进入catch TimeoutException");
7.自定义拒绝策略RejectedExecutionHandler接口的使用
接口RejectedExecutionHandler的主要作用是当线程池关闭后依然有任务要执行时,可以实现一些处理。
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 + " 在运行!");
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()的区别
(1) 方法execute()没有返回值,而submit()方法可以有返回值。
(2) 方法execute()在默认的情况下异常直接抛出,不能捕获,但可以通过自定义ThreadFactory的方式进行捕获,而submit()方法在默认的情况下,可以catch Execution-Exeception捕获异常
9.验证Future的缺点

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()); // 说明一个Future对应指定的一个Callable } catch (InterruptedException e) { } catch (ExecutionException e) {
总结:Future和Callable这两个接口的优点就是从线程中返回数据以便进行后期的处理,但是FutureTask类也有其自身的缺点,就是阻塞性。
|