Java多线程编程中,常用的多线程设计模式包括:Future模式、Master-Worker模式、Guarded Suspeionsion模式、不变模式和生产者-消费者模式。
Future模式核心思想
Future模式的核心在于:去除了主函数的等待时间,并使得原本需要等待的时间段可以用于处理其他业务逻辑。
Future模式的基本原理
案例一:Java实现Future模式
Client.java:
public class Client {
public Data request(final String string) {
final FutureData futureData=new FutureData();
new Thread(new Runnable() {
@Override
public void run() {
//RealData的构建很慢,所以放在单独的线程中运行,可以立即返回futureData
RealData realData=new RealData(string);
futureData.setRealData(realData);
}
}).start();
return futureData;
}
}
FutureData.java:作为RealData类的代理
public class FutureData implements Data<String>{
RealData realData = null;
//FutureData是RealData的封装
boolean isReady = false; //是否已经准备好
public synchronized void setRealData(RealData realData) {
if(isReady) {
return;
}
this.realData=realData;
isReady=true;
notifyAll(); //RealData已经被注入到FutueData中了,通知getResult()方法
}
@Override
public String getResult() throws InterruptedException {
if(!isReady) {
wait();//一直等到RealData注入到FutureData中
}
return realData.getResult();
}
}
RealData.java
public class RealData implements Data<String> {
protected String data;
public RealData(String data) {
// 利用sleep方法来表示真是业务是非常缓慢的
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
}
@Override
public String getResult() throws InterruptedException {
return data;
}
}
Data.java
public interface Data<String> {
String getResult() throws InterruptedException;
}
Application.java
public class Application {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Client client = new Client();
//这里会立即返回,因为获取的是FutureData
Data data=client.request("name");
Thread.sleep(2000);
//使用真实数据
System.out.println("数据="+data.getResult());
}
}
案例二:JDK的内置Future实现
RealData.java
public class RealData implements Callable<String>{
protected String data;
public RealData(String data) {
this.data = data;
}
@Override
public String call() throws Exception {
//利用sleep方法来表示真是业务是非常缓慢的
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return data;
}
}
Application.java
public class Application {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> futureTask=new FutureTask<>(new RealData("name"));
ExecutorService executorService=Executors.newFixedThreadPool(1); executorService.submit(futureTask);
Thread.sleep(2000);
//使用真实数据
//如果call()没有执行完成依然会等待
System.out.println("数据="+futureTask.get());
}
}
Callable、Future原理解析
Executor框架使用Runnable作为其基本的任务表示形式。Runnable中的run方法不会返回一个值或抛出一个受检查的异常。
public interface Runnable {
public abstract void run();
}
许多任务实际上都是存在延迟的计算,对于这些任务,Callable是一种更好的抽象:它的call方法会返回一个值,并且可能抛出一个异常。但实际上call底层还是调用了run方法,具体的细节在后面的线程池章节介绍。
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
* *
* * @return computed result
* * @throws Exception if unable to compute a result
* */
V call() throws Exception;
}
Runnable和Callable描述的都是抽象的计算任务。Executor执行的任务有四个阶段:创建,提交,开始和完成。由于有些任务可能要执行很长的时间,因此通常希望可以取消某些任务。在Executor框架中,已提交但尚未开始的任务可以取消,对于已经开始执行的任务,只有当它们响应中断时才能取消。
Future表示一个任务的生命周期,并提供了方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
在Future接口声明了5个方法:
- cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
- isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
- isDone方法表示任务是否已经完成,若任务完成,则返回true;
- get()方法用来获取call方法的执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
- get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。