详细参见葛一名老师的《Java程序性能优化》
Futrue模式:对于多线程,如果线程A要等待线程B的结果,那么线程A没必要等待B,直到B有结果,可以先拿到一个未来的Future,等B有结果是再取真实的结果。
在多线程中经常举的一个例子就是:网络图片的下载,刚开始是通过模糊的图片来代替最后的图片,等下载图片的线程下载完图片后在替换。而在这个过程中可以做一些其他的事情。
首先客户端向服务器请求RealSubject,但是这个资源的创建是非常耗时的,怎么办呢?这种情况下,首先返回Client一个FutureSubject,以满足客户端的需求,于此同时呢,Future会通过另外一个Thread 去构造一个真正的资源,资源准备完毕之后,在给future一个通知。如果客户端急于获取这个真正的资源,那么就会阻塞客户端的其他所有线程,等待资源准备完毕。
公共数据接口,FutureData和RealData都要实现。
public interface Data {
public abstract String getContent();
}
FutureData,当有线程想要获取RealData的时候,程序会被阻塞。等到RealData被注入才会使用getReal()方法。
package com.volshell.future; public class FutureData implements Data { protected RealData realData = null;
protected boolean isReady = false; @Override
public synchronized String getResult() {
// TODO Auto-generated method stub
while (!isReady) {
try {
wait();
} catch (Exception e) {
// TODO: handle exception
}
}
return realData.result;
} public synchronized void setRealData(RealData realData) {
if (isReady)
return;
this.realData = realData;
isReady = true;
notifyAll();
}
}
真实数据RealData
package com.volshell.future; public class RealData implements Data {
protected String result; public RealData(String para) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
sb.append(para);
try {
Thread.sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
result = sb.toString();
}
} @Override
public String getResult() {
// TODO Auto-generated method stub
return result;
} }
客户端程序:
package com.volshell.future; public class Client {
public Data request(final String request){
final FutureData future = new FutureData();
new Thread(){
public void run() { RealData reaData = new RealData(request);
future.setRealData(reaData);
};
}.start();
return future;
}
}
调用者:
package com.volshell.future; public class Main {
public static void main(String[] args) {
Client client = new Client();
Data data = client.request("name");
System.out.println("请求完毕!!");
try {
Thread.sleep(2000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("获取的数据:" +data.getResult());
}
}
调用者请求资源,client.request("name"); 完成对数据的准备
当要获取资源的时候,data.getResult() ,如果资源没有准备好isReady = false;那么就会阻塞该线程。直到资源获取然后该线程被唤醒。
今天又重新了解了future模式。
package com.volshell.future2; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class FutureTest2 {
private static class Task implements Callable<String> {
@Override
public String call() throws Exception {
// 模拟真实事务的处理过程,这个过程是非常耗时的。
Thread.sleep(5000);
return "call return ";
}
} public static void main(String[] args) throws InterruptedException,
ExecutionException {
List<Future<String>> futures = new ArrayList<Future<String>>();
ExecutorService executorService = Executors.newCachedThreadPool(); System.out.println("已经提交资源申请");
for (int i = 0; i < 10; i++) {
futures.add(executorService.submit(new Task()));
} for (Future<String> future : futures) {
// 判断资源是不是已经准备完毕,准备完毕直接获取。
if (!future.isDone()) {
System.out.println("资源还没有准备好");
}
System.out.println(future.get());
}
executorService.shutdown();
}
}
其中的核心就是Callable中的call方法,这个和Runnable中的run 非常类似。
Runnable和Callable都是接口
不同之处:
1.Callable可以返回一个类型V,而Runnable不可以
2.Callable能够抛出checked exception,而Runnable不可以。
3.Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的
4.Callable和Runnable都可以应用于executors。而Thread类只支持Runnable.
上面只是简单的不同,其实这两个接口在用起来差别还是很大的。Callable与executors联合在一起,在任务完成时可立刻获得一个更新了的Future。而Runable却要自己处理
Future接口,一般都是取回Callable执行的状态用的。其中的主要方法:
- cancel,取消Callable的执行,当Callable还没有完成时
- get,获得Callable的返回值
- isCanceled,判断是否取消了
- isDone,判断是否完成
用Executor来构建线程池,应该要做的事:
1).调用Executors类中的静态方法newCachedThreadPool(必要时创建新
线程,空闲线程会被保留60秒)或newFixedThreadPool(包含固定数量的线程池)等,返回的是一个实现了ExecutorService
接口的ThreadPoolExecutor类或者是一个实现了ScheduledExecutorServiece接口的类对象。
2).调用submit提交Runnable或Callable对象。
3).如果想要取消一个任务,或如果提交Callable对象,那就要保存好返回的Future对象。
4).当不再提交任何任务时,调用shutdown方法。