配色: 字号:
kmdjs和循环依赖
2016-09-23 | 阅:  转:  |  分享 
  
Future模式


线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascript,页面渲染等操作,当我们使用ajax向服务端发起请求,由于这个过程很慢,ajax的异步模式可以让我们无需一直等待服务端的响应,而在这个等待结果时间里做其他的事情,这个模式在线程技术力称之为Future模式。

Future模式和我前面文章里说到的html5技术里的worker技术差不多,当我们一个程序执行流里某个操作特别耗时,我们可以另起一个线程专门执行这个繁琐耗时的任务,主线程则可以做其他的事情,下面是我自己找到的一个实现原生Future模式的代码,它主要参入者如下:

TestMain.java:测试程序入口,主要是调用Client类,向Client发送请求;

Client.java:返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData;

Data.java:返回数据接口;

FutureData.java:Future数据,构造快,但是是一个虚拟的数据,需要装配RealData;

RealData.java:真实数据,其构造是比较慢的。

详细代码如下:

Data接口:

packagecn.com.xSharp.futurePattern.simple;

/
数据接口
@author俊

/
publicinterfaceData{
publicStringgetData();
}
RealData代码:

packagecn.com.xSharp.futurePattern.simple;

/
RealData是最终使用的数据,它构造很慢,因此用sleep来模拟
@author俊
@since2016-06-2121:37
/
publicclassRealDataimplementsData{

protectedfinalStringresult;


publicRealData(Stringparam){
StringBuffersb=newStringBuffer();
for(inti=0;i<10;i++){
sb.append(param);
try{
Thread.sleep(100);
}catch(Exceptione){
e.printStackTrace();
}
}
result=sb.toString();
}



@Override
publicStringgetData(){
returnresult;
}

}
FutureData代码:
packagecn.com.xSharp.futurePattern.simple;

publicclassFutureDataimplementsData{

protectedRealDatarealData=null;//FutureData对RealData的包装
protectedbooleanisReady=false;

publicsynchronizedvoidsetRealData(RealDatarealData){
if(isReady){
return;
}
this.realData=realData;
isReady=true;
notifyAll();
}

@Override
publicsynchronizedStringgetData(){
while(!isReady){
try{
wait();
}catch(Exceptione){
e.printStackTrace();
}
}
returnrealData.result;
}

}
Client代码:
packagecn.com.xSharp.futurePattern.simple;

publicclassClient{

publicDatarequest(finalStringqryStr){
finalFutureDatafutureData=newFutureData();
newThread(){
publicvoidrun(){
RealDatarealData=newRealData(qryStr);
futureData.setRealData(realData);
}
}.start();
returnfutureData;
}
}
TestMain代码:

packagecn.com.xSharp.futurePattern.simple;

publicclassTestMain{

publicstaticvoidmain(String[]args){
Clientclient=newClient();
Datadata=client.request("xtq");
System.out.println("请求完毕!");

try{
for(inti=0;i<12;i++){
Thread.sleep(100);
System.out.println("可以做做其他的事情哦....");
}

}catch(InterruptedExceptione){
e.printStackTrace();
}

System.out.println("数据==:"+data.getData());
}

}
执行结果:
请求完毕!
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
数据==:xtqxtqxtqxtqxtqxtqxtqxtqxtqxtq
JDK里在1.5之后提供了专门Future模式的实现,这里我使用FutureTask来实现Future模式。

FutureTask在JDK文档里的解释:



可取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,此类提供了对Future的基本实现。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞get方法。一旦计算完成,就不能再重新开始或取消计算。可使用FutureTask包装Callable或Runnable对象。因为FutureTask实现了Runnable,所以可将FutureTask提交给Executor执行。除了作为一个独立的类外,此类还提供了protected功能,这在创建自定义任务类时可能很有用。



下面是它的两个构造函数:

1
2
3
4
FutureTask(Callablecallable)
创建一个FutureTask,一旦运行就执行给定的Callable。
FutureTask(Runnablerunnable,Vresult)
创建一个FutureTask,一旦运行就执行给定的Runnable,并安排成功完成时get返回给定的结果。
这里我首先使用第二个构造函数Runnable实现Future模式,代码如下:
packagecn.com.futuretest;

importjava.util.concurrent.ExecutionException;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.FutureTask;

publicclassFutureRunnableimplementsRunnable{
privateResultresult;//操作的数据,模拟一个计算需要很长时间的数据

/初始化数据/
publicFutureRunnable(Resultresult){
this.result=result;
}

@Override
publicvoidrun(){
try{
for(inti=0;i<10;i++){
Thread.sleep(100);//每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
result.setData(result.getData()+":"+"futureRunnable"+i);
}
}catch(InterruptedExceptione){
e.printStackTrace();
}
}

publicstaticvoidmain(String[]args){
Resultr=newResult("xSharp");//构造测试数据
FutureRunnablefutureCallable=newFutureRunnable(r);//初始化runnable
FutureTasktask=newFutureTask(futureCallable,r);
//构造固定大小为一个线程的线程池
ExecutorServiceexecutorService=Executors.newFixedThreadPool(1);
//执行线程
executorService.execute(task);
System.out.println("执行完毕!");

try{
for(inti=0;i<15;i++){
Thread.sleep(100);
System.out.println("数据还在计算中等待中,你可以做别的事情"+i);
}
}catch(InterruptedExceptione){
e.printStackTrace();
}

try{
System.out.println("打印结果是:"+task.get().getData());
}catch(InterruptedExceptione){
e.printStackTrace();
}catch(ExecutionExceptione){
e.printStackTrace();
}finally{
System.exit(0);
}

}

}
执行结果:

执行完毕!
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
数据还在计算中等待中,你可以做别的事情6
数据还在计算中等待中,你可以做别的事情7
数据还在计算中等待中,你可以做别的事情8
数据还在计算中等待中,你可以做别的事情9
数据还在计算中等待中,你可以做别的事情10
数据还在计算中等待中,你可以做别的事情11
数据还在计算中等待中,你可以做别的事情12
数据还在计算中等待中,你可以做别的事情13
数据还在计算中等待中,你可以做别的事情14
打印结果是:xSharp:futureRunnable0:futureRunnable1:futureRunnable2:futureRunnable3:futureRunnable4:futureRunnable5:futureRunnable6:futureRunnable7:futureRunnable8:futureRunnable9
接下来我使用Callable接口实现FutureTask,代码如下:
packagecn.com.futuretest;

importjava.util.concurrent.Callable;
importjava.util.concurrent.ExecutionException;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.FutureTask;

publicclassFutureCallableimplementsCallable{

privateResultresult;//操作的数据,模拟一个计算需要很长时间的数据

/初始化数据/
publicFutureCallable(Resultresult){
this.result=result;
}

@Override
publicResultcall()throwsException{
try{
for(inti=0;i<10;i++){
Thread.sleep(100);//每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
result.setData(result.getData()+":"+"futureCallable"+i);
}
}catch(InterruptedExceptione){
e.printStackTrace();
}
returnresult;
}

publicstaticvoidmain(String[]args){
longstart=System.currentTimeMillis();
Resultr=newResult("xSharp");//构造测试数据
FutureCallablecallable=newFutureCallable(r);
FutureTasktask=newFutureTask(callable);
//构造固定大小为一个线程的线程池
ExecutorServiceexecutorService=Executors.newFixedThreadPool(1);
//执行线程
executorService.execute(task);
System.out.println("执行完毕!");
longcurr01=System.currentTimeMillis();
System.out.println("任务提交后的耗时:"+(curr01-start)+"毫秒");
try{
for(inti=0;i<6;i++){
Thread.sleep(100);
System.out.println("数据还在计算中等待中,你可以做别的事情"+i);
}
}catch(InterruptedExceptione){
e.printStackTrace();
}

try{
System.out.println("打印结果是:"+task.get().getData());
longend=System.currentTimeMillis();
System.out.println("总耗时:"+(end-start)+"毫秒");
}catch(InterruptedExceptione){
e.printStackTrace();
}catch(ExecutionExceptione){
e.printStackTrace();
}finally{
System.exit(0);
}
}

}
执行结果如下:
执行完毕!
任务提交后的耗时:6毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
打印结果是:xSharp:futureCallable0:futureCallable1:futureCallable2:futureCallable3:futureCallable4:futureCallable5:futureCallable6:futureCallable7:futureCallable8:futureCallable9
总耗时:1010毫秒
这里我对代码做了一些调整,一个是加上了执行时间的统计,一个是我将干其他事情的程序执行时间变短,小于了线程本身执行的时间,这么做的目的是想和下面的程序对比,下面的代码当我执行线程后没有做其他的操作,而是直接获取线程执行的结果,具体代码如下:
packagecn.com.futuretest;

importjava.util.concurrent.Callable;
importjava.util.concurrent.ExecutionException;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.FutureTask;

publicclassNioFutureCallableimplementsCallable{

privateResultresult;//操作的数据,模拟一个计算需要很长时间的数据

/初始化数据/
publicNioFutureCallable(Resultresult){
this.result=result;
}

@Override
publicResultcall()throwsException{
try{
for(inti=0;i<10;i++){
Thread.sleep(100);//每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
result.setData(result.getData()+":"+"NioFutureCallable"+i);
}
}catch(InterruptedExceptione){
e.printStackTrace();
}
returnresult;
}

publicstaticvoidmain(String[]args){
longstart=System.currentTimeMillis();
Resultr=newResult("xSharp");//构造测试数据
NioFuturewww.wang027.comCallablecallable=newNioFutureCallable(r);
FutureTasktask=newFutureTask(callable);
//构造固定大小为一个线程的线程池
ExecutorServiceexecutorService=Executors.newFixedThreadPool(1);
//执行线程
executorService.execute(task);
System.out.println("执行完毕!");
longcurr01=System.currentTimeMillis();
System.out.println("任务提交后的耗时:"+(curr01-start)+"毫秒");

/第一次获取返回数据/
try{
System.out.println("第一次打印结果是:"+task.get().getData());
longcurr02=System.currentTimeMillis();
System.out.println("第一次获取结果耗时:"+(curr02-start)+"毫秒");
}catch(InterruptedExceptione1){
e1.printStackTrace();
}catch(ExecutionExceptione1){
e1.printStackTrace();
}

try{
for(inti=0;i<10;i++){
Thread.sleep(100);
System.out.println("数据还在计算中等待中,你可以做别的事情"+i);
}
}catch(InterruptedExceptione){
e.printStackTrace();
}

try{
System.out.println("第二次打印结果是:"+task.get().getData());
longend=System.currentTimeMillis();
System.out.println("总耗时:"+(end-start)+"毫秒");
}catch(InterruptedExceptione){
e.printStackTrace();
}catch(ExecutionExceptione){
e.printStackTrace();
}finally{
System.exit(0);
}


}

}

第一次打印结果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9
第一次获取结果耗时:1009毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
数据还在计算中等待中,你可以做别的事情6
数据还在计算中等待中,你可以做别的事情7
数据还在计算中等待中,你可以做别的事情8
数据还在计算中等待中,你可以做别的事情9
第二次打印结果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9
总耗时:2012毫秒
我们看到当我们直接获取结果时候,整个主线程都被阻塞了,直到结果返回后才会执行下面的后续操作,这也就是说如果计算还没结束,我们就想获取结果这样整个执行流程都将被阻塞,这点在我们合理使用Future模式时候很重要。

除了使用FutureTask实现Future模式,我们还可以使用ExecutorService的submit方法直接返回Future对象,Future就和我前面设计的原生Future类似,当我们开始调用时候返回的是一个虚拟结果,其实实际的计算还没有结束,只有等待吗一会儿后结果才会真正的返回,代码如下:

packagecn.com.futuretest;

importjava.util.concurrent.Callable;
importjava.util.concurrent.ExecutionException;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.Future;

publicclassRetFutureCallableimplementsCallable{

privateResultresult;//操作的数据,模拟一个计算需要很长时间的数据

publicRetFutureCallable(){
result=newResult("xSharp");
}

@Override
publicResultcall()throwsException{
try{
for(inti=0;i<10;i++){
Thread.sleep(100);//每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
result.setData(result.getData()+":"+"RetFutureCallable"+i);
}
}catch(InterruptedExceptione){
e.printStackTrace();
}
returnresult;
}

publicstaticvoidmain(String[]args){
longstart=System.currentTimeMillis();
RetFutureCallablecallable=newRetFutureCallable();
//构造固定大小为一个线程的线程池
ExecutorServiceexecutorService=Executors.newFixedThreadPool(1);
//执行线程
Futurer=executorService.submit(callable);
System.out.println("执行完毕!");
longcurr01=System.currentTimeMillis();
System.out.println("任务提交后的耗时:"+(curr01-start)+"毫秒");
try{
for(inti=0;i<6;i++){
Thread.sleep(100);
System.out.println("数据还在计算中等待中,你可以做别的事情"+i);
}
}catch(InterruptedExceptione){
e.printStackTrace();
}

try{
System.out.println("打印结果是:"+r.get().getData());
longend=System.currentTimeMillis();
System.out.println("总耗时:"+(end-start)+"毫秒");
}catch(InterruptedExceptione){
e.printStackTrace();
}catch(ExecutionExceptione){
e.printStackTrace();
}finally{
System.exit(0);
}
}


}
执行结果如下:

1
2
3
4
5
6
7
8
9
10
执行完毕!
任务提交后的耗时:5毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
打印结果是:xSharp:RetFutureCallable0:RetFutureCallable1:RetFutureCallable2:RetFutureCallable3:RetFutureCallable4:RetFutureCallable5:RetFutureCallable6:RetFutureCallable7:RetFutureCallable8:RetFutureCallable9
总耗时:1006毫秒
好了,本文写完了。
献花(0)
+1
(本文系thedust79首藏)