最近的项目用到了多线程,发现java.util.concurrent.Future蛮好用的。
像平时,写多线程一般使用Thread/Runnable,直接扔给线程池执行就好了。但是遇到了一些需要获取线程执行结果的情况,就需要使用Callable。对于使用Callable的task,ExecutorService执行后会返回一个Future对象来传递执行结果。
那这个Future是怎么存放返回值的呢,这个Future有什么值得使用的地方呢。我查了一下,发现原来还专门有个Future模式。
Future模式
Future模式在请求发生时,会先产生一个Future凭证给发出请求的客户,它的作用就像是Proxy物件,同时,由一个新的执行线程持续进行目标物件的生成(Thread-Per-Message),真正的目标物件生成之后,将之设定至Future之中,而当客户端真正需要目标物件时,目标物件也已经准备好,可以让客户提取使用。
结合JDK的Future来看,就是你run线程后,你可以把线程的返回值赋给Future并返回一个Future对象。这时你可以立即拿到这个对象,然后进行下面的逻辑。但是如果你要get这个Future中的线程结果,就会被阻塞直到线程结束。
就相当于现在的期房,你把手续和钱都交上去了,就可以马上拿到合同,但只有合同没有房子。这个时候你已经是有房一族了,你可以先去买家电买装修(走下面的其他逻辑)。但是你要把家电和装修放进去,就必须等到房子完工(阻塞)。
这样有一个好处,就是你把处理任务交给线程并拿到那个future凭证,就可以去干别的事情了(同时线程也在处理),等你真正要用到这个线程返回值的时候再通过future来获取,这样能缩短阻塞的时间。
Demo
接下来,我们看看Future的一个demo,这个例子很简单,就是为表明线程提交处理后,future对象是立即返回的,紧接着“Ready”马上就打印“Give the future”了;而当调用future.get的时候就停下了,“Get the future”迟迟没有打印,被阻塞了,直到线程执行完毕为止。这里我让他睡了3秒钟,能看得更清楚。
public class FutureTest { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newCachedThreadPool(); System.out.println("Ready"); Future strFuture = executor.submit(new TaskTest()); System.out.println("Give the future"); System.out.println("Get the future : " + strFuture.get()); System.out.println("End"); executor.shutdown(); } public static class TaskTest implements Callable { @Override public String call() throws Exception { Thread.sleep(3000); return "Hello World!"; } } }
Simulator
从future的应用能看出来,其实future实现的功能很简单,就是充当一个线程返回结果的寄存器,只是在获取结果的时候,检查线程是否已完成,还在处理则阻塞,否则返回结果。
下面我做了一个future的模拟实现:
首先future作为一个结果寄存器,就需要有个result来存放结果,另外需要一个isFinish来标记线程是否完成。当然我在set()的时候不仅给result赋值,还把isFinish标记成完成,因为一般返回值都在线程结束时赋值的,所以我也这么简化设计了。
测试使用上面的demo,运行结果也是一样的。只是我的这个future没有JDK那个优雅和健壮,那个还整合到ExecutorService,用起来更方便。
【
所以,还有请大神们指导,这样的future实现,是不是还有什么漏洞?】
public class FutureSimulator { private volatile T result; private volatile boolean isFinish = false; public void set(T result) { this.result = result; this.isFinish = true; } public T get() throws InterruptedException { while (!isFinish) { Thread.sleep(100); } return result; } public static void main(String[] args) throws Exception { FutureSimulator future = new FutureSimulator(); ExecutorService executor = Executors.newCachedThreadPool(); System.out.println("Ready"); executor.submit(new FutureTaskTest(future)); System.out.println("Give the future"); System.out.println("Get the future : " + future.get()); System.out.println("End"); executor.shutdown(); } } class FutureTaskTest extends Thread { private FutureSimulator future; public FutureTaskTest(FutureSimulator future) { this.future = future; } @Override public void run(){ try { Thread.sleep(3000); future.set("Hello World!"); } catch (InterruptedException e) { e.printStackTrace(); } } }