在默认的情况下,线程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()用于获得返回值,示例如下:
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();
}
}
}