Future和Callable的使用总结
在默认的情况下,线程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)的使用
方法submit(Callable)可以执行参数为Callable的任务,方法get()用于获得返回值,示例如下:
package mycallable; import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { private int age; public MyCallable(int age) { super(); this.age = age; } public String call() throws Exception { Thread.sleep(8000); return "返回值 年龄是:" + age; } }
package test.run; 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 class Run { public static void main(String[] args) throws InterruptedException { try { 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) { e.printStackTrace(); } } }
3.方法get()结合ExecutorService中的submit(Runnable)和isDone()的使用
方法submit()不仅可以传入Callable对象,也可以传入Runnable对象,说明submit方法支持有返回值和无返回值的功能。如果submit方法传入Callable接口则可以有返回值,如果传入Runnable则无返回值,打印的结果就是null。方法get()具有阻塞特性,而isDone()方法无阻塞特性。
package test.run; 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()方法无阻塞特性 * @author linhaiy * @date 2019.03.06 */ public class Run { public static void main(String[] args) { try { Runnable runnable = new Runnable() { @Override public void run() { System.out.println("打印的信息"); } }; ExecutorService executorRef = Executors.newCachedThreadPool(); Future future = executorRef.submit(runnable); System.out.println(future.get() + " " + future.isDone()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
4.使用ExecutorService接口中的方法submit(Runnable,T result)
方法submit(Runnable,T result)的第2个参数result可以作为执行结果的返回值,而不需要使用get()方法来进行获得。
package entity; public class Userinfo { private String username; private String password; public Userinfo() { super(); } public Userinfo(String username, String password) { super(); 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; } }
package myrunnable; import entity.Userinfo; public class MyRunnable implements Runnable { private Userinfo userinfo; public MyRunnable(Userinfo userinfo) { super(); this.userinfo = userinfo; } @Override public void run() { userinfo.setUsername("usernameValue"); userinfo.setPassword("passwordValue"); } }
package test; 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; import entity.Userinfo; public class Test { FutureTask abc; //接口Future的实现类 public static void main(String[] args) { try { 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()); userinfo = future.get(); System.out.println("get value " + userinfo.getUsername() + " " + userinfo.getPassword()); System.out.println(" end time=" + System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
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)的作用是在指定的最大时间内等待获得返回值。
package mycallable; import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { public String call() throws Exception { Thread.sleep(10000); //目的是为了在指定时间内获取不到返回值 System.out.println("sleep 10秒执行完了!"); return "anyString"; } }
package test.run; 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 class Run { public static void main(String[] args) { try { 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"); e.printStackTrace(); } catch (ExecutionException e) { System.out.println("进入catch ExecutionException"); e.printStackTrace(); } catch (TimeoutException e) { System.out.println("进入catch TimeoutException"); e.printStackTrace(); } } }
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 { private String username; public MyRunnable(String username) { super(); this.username = username; } public void run() { System.out.println(username + " 在运行!"); } }
package com.executionhandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class Run { 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")); executor.shutdown(); service.submit(new MyRunnable("D")); } }
8.方法execute()与submit()的区别
(1) 方法execute()没有返回值,而submit()方法可以有返回值。
(2) 方法execute()在默认的情况下异常直接抛出,不能捕获,但可以通过自定义ThreadFactory的方式进行捕获,而submit()方法在默认的情况下,可以catch Execution-Exeception捕获异常
9.验证Future的缺点

package mycallable; import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { private String username; private long sleepValue; public MyCallable(String username, long sleepValue) { super(); this.username = username; this.sleepValue = sleepValue; } @Override public String call() throws Exception { System.out.println(username); Thread.sleep(sleepValue); return "return " + username; } }
package test; import java.util.ArrayList; import java.util.List; 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 class Test { public static void main(String[] args) { try { 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) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
总结
Future和Callable这两个接口的优点就是从线程中返回数据以便进行后期的处理,但是FutureTask类也有其自身的缺点,就是阻塞性。
