在多线程交互的中,经常有一个线程需要得到另个一线程的计算结果,我们常用的是Future异步模式来加以解决。
什么是Future模式呢?Future 顾名思义,在金融行业叫期权,市场上有看跌期权和看涨期权,你可以在现在(比如九月份)购买年底(十二月)的石油,假如你买的是看涨期权,那么如果石油真 的涨了,你也可以在十二月份依照九月份商定的价格购买。扯远了,Future就是你可以拿到未来的结果。对于多线程,如果线程A要等待线程B的结果,那么 线程A没必要等待B,直到B有结果,可以先拿到一个未来的Future,等B有结果是再取真实的结果。其实这个模式用的很多,比如浏览器下载图片的时候, 刚开始是不是通过模糊的图片来代替最后的图片,等下载图片的线程下载完图片后在替换。如图所示:
GenerateResultThread开始进行计算了!
过来2s了,看看有结果吗?
还没有完成呢! 继续干自己活吧!
过来4s了,看看有结果吗?
完成了!
Result:ResultByGenerateResultThread
注意: 通过JDK标准的Future后,没有必要增加额外的Object来只有Result,更加简单明了,同时FutureTask还提供了Cancel的功 能,我们持有FutureTask引用后可以Cancel该线程。通过get()取值是,如果结果还没有返回,将会阻塞Main线程。
看看,当 new Thread(new FutureTask(new Callable())).start 时:
看图:
G
get 方法取result值,FutureTask 提供Timeout 功能,如果超时,抛出异常。
什么是Future模式呢?Future 顾名思义,在金融行业叫期权,市场上有看跌期权和看涨期权,你可以在现在(比如九月份)购买年底(十二月)的石油,假如你买的是看涨期权,那么如果石油真 的涨了,你也可以在十二月份依照九月份商定的价格购买。扯远了,Future就是你可以拿到未来的结果。对于多线程,如果线程A要等待线程B的结果,那么 线程A没必要等待B,直到B有结果,可以先拿到一个未来的Future,等B有结果是再取真实的结果。其实这个模式用的很多,比如浏览器下载图片的时候, 刚开始是不是通过模糊的图片来代替最后的图片,等下载图片的线程下载完图片后在替换。如图所示:
- 在没有JDK1.5提供的Concurrent之前,我们通过自定义一个结果类,负责结果持有。
package
vincent.blogjava.net;
public class FutureResult {
private String result;
private boolean isFinish = false ;
public String getResult() {
return result;
}
public synchronized void setResult(String result) {
this .result = result;
this .isFinish = true ;
}
public synchronized boolean isFinish() {
return isFinish;
}
}
存储结果值和是否完成的Flag。
public class FutureResult {
private String result;
private boolean isFinish = false ;
public String getResult() {
return result;
}
public synchronized void setResult(String result) {
this .result = result;
this .isFinish = true ;
}
public synchronized boolean isFinish() {
return isFinish;
}
}
package
vincent.blogjava.net;
public class GenerateResultThread extends Thread{
FutureResult fr ;
public GenerateResultThread(FutureResult fr ){
this .fr = fr;
}
public void run(){
// 模仿大量耗时计算后(5s)返回结果。
try {
System.out.println( " GenerateResultThread开始进行计算了! " );
Thread.sleep( 5000 );
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fr.setResult( " ResultByGenerateResultThread " );
}
}
计算具体业务逻辑并放回结果的线程。
public class GenerateResultThread extends Thread{
FutureResult fr ;
public GenerateResultThread(FutureResult fr ){
this .fr = fr;
}
public void run(){
// 模仿大量耗时计算后(5s)返回结果。
try {
System.out.println( " GenerateResultThread开始进行计算了! " );
Thread.sleep( 5000 );
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fr.setResult( " ResultByGenerateResultThread " );
}
}
package
vincent.blogjava.net;
public class Main {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
FutureResult fr = new FutureResult();
new GenerateResultThread(fr).start();
// main线程无需等待,不会被阻塞。
// 模仿 干自己的活 2s。
Thread.sleep( 2000 );
// 估计算完了吧 取取试试。
System.out.println( " 过来2s了,看看有结果吗? " );
if ( ! fr.isFinish()){System.out.println( " 还没有完成呢! 继续干自己活吧! " );}
// 模仿 干自己的活 4s。
Thread.sleep( 4000 );
System.out.println( " 过来4s了,看看有结果吗? " );
if (fr.isFinish()){
System.out.println( " 完成了! " );
System.out.println( " Result: " + fr.getResult());
}
}
}
Main方法需要
GenerateResultThread线程计算的结果,通过这种模式,main线程不需要阻塞。结果如下:
public class Main {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
FutureResult fr = new FutureResult();
new GenerateResultThread(fr).start();
// main线程无需等待,不会被阻塞。
// 模仿 干自己的活 2s。
Thread.sleep( 2000 );
// 估计算完了吧 取取试试。
System.out.println( " 过来2s了,看看有结果吗? " );
if ( ! fr.isFinish()){System.out.println( " 还没有完成呢! 继续干自己活吧! " );}
// 模仿 干自己的活 4s。
Thread.sleep( 4000 );
System.out.println( " 过来4s了,看看有结果吗? " );
if (fr.isFinish()){
System.out.println( " 完成了! " );
System.out.println( " Result: " + fr.getResult());
}
}
}
GenerateResultThread开始进行计算了!
过来2s了,看看有结果吗?
还没有完成呢! 继续干自己活吧!
过来4s了,看看有结果吗?
完成了!
Result:ResultByGenerateResultThread
- 在JDK1.5 Concurrent 中,提供了这种Callable的机制。我们只要实现Callable接口中的Call方法,Call方法是可以返回任意类型的结果的。如下:
package
vincent.blogjava.net;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ConcurrentImpl {
public static void main(String[] args) throws InterruptedException, Exception {
FutureTask fr = new FutureTask( new Returnresult());
new Thread(fr).start();
// main线程无需等待,不会被阻塞。
// 模仿 干自己的活 2s。
Thread.sleep( 2000 );
// 估计算完了吧 取取试试。
System.out.println( " 过来2s了,看看有结果吗? " );
if ( ! fr.isDone()){System.out.println( " 还没有完成呢! 继续干自己活吧! " );}
// 模仿 干自己的活 4s。
Thread.sleep( 4000 );
System.out.println( " 过来4s了,看看有结果吗? " );
if (fr.isDone()){
System.out.println( " 完成了! " );
System.out.println( " Result: " + fr.get());
}
}
}
class Returnresult implements Callable{
@Override
public Object call() throws Exception {
// 模仿大量耗时计算后(5s)返回结果。
System.out.println( " GenerateResultThread开始进行计算了! " );
Thread.sleep( 11000 );
return " ResultByGenerateResultThread " ;
}
}
Returnresult 实现了Callable接口,在Call方法中实现业务逻辑,并返回结果。在Main方法里面,
初始化FutureTask 并将该Task作为Runnable加入Thread后,启动线程。得到跟刚才相同的效果。import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ConcurrentImpl {
public static void main(String[] args) throws InterruptedException, Exception {
FutureTask fr = new FutureTask( new Returnresult());
new Thread(fr).start();
// main线程无需等待,不会被阻塞。
// 模仿 干自己的活 2s。
Thread.sleep( 2000 );
// 估计算完了吧 取取试试。
System.out.println( " 过来2s了,看看有结果吗? " );
if ( ! fr.isDone()){System.out.println( " 还没有完成呢! 继续干自己活吧! " );}
// 模仿 干自己的活 4s。
Thread.sleep( 4000 );
System.out.println( " 过来4s了,看看有结果吗? " );
if (fr.isDone()){
System.out.println( " 完成了! " );
System.out.println( " Result: " + fr.get());
}
}
}
class Returnresult implements Callable{
@Override
public Object call() throws Exception {
// 模仿大量耗时计算后(5s)返回结果。
System.out.println( " GenerateResultThread开始进行计算了! " );
Thread.sleep( 11000 );
return " ResultByGenerateResultThread " ;
}
}
注意: 通过JDK标准的Future后,没有必要增加额外的Object来只有Result,更加简单明了,同时FutureTask还提供了Cancel的功 能,我们持有FutureTask引用后可以Cancel该线程。通过get()取值是,如果结果还没有返回,将会阻塞Main线程。
- 其实JDK 实现Future模式的秘密就在FutureTask类里:
看看,当 new Thread(new FutureTask(new Callable())).start 时:
看图:
G
get 方法取result值,FutureTask 提供Timeout 功能,如果超时,抛出异常。