完成前面两篇文章的代码训练后,继续往深层进发. 线程的编程模式打破了传统的代码顺序执行的习惯. 两个线程之间的交互也改变了原来的调用-返回值模式. 同样的,javascript的ajax callback方式也有着同样的痛苦. 为了让程序员保留原来的编程习惯,也为了让代码逻辑更容易读懂. 多线程计算提供了两种模式: Future和Promise.
Promise
Promise是多线程的非阻塞模式.但有些类似callback. 它在javascript中得到了很好的应用.比如,dojo.promise. 每一个ajax HTTP request返回一个promise,许诺在ajax线程返回结果以后,运行promise.then()中的代码函数.这样就主线程可以越过then(),继续向下执行,而不用等待ajax线程的返回.
function() {
promise = googleRequest();
promise.then(function(result){
handleResult();
});
doOtherWorks();
} function发起googleRequest线程, 线程promise在结果返回以后将执行then()中的代码. function得到promise,安心的去执行doOtherWorks,而不用等待googleRequest的返回了.
dojo.promise.all还可以同时管理多个promise.如下,function发起3个异步请求,3个请求相互独立. all将管理3个promise,一旦3个异步请求全部完成,将执行then中的代码. 主线程function安心的去doOtherWorks,而不用等待异步请求.
function() {
promise1 = googleRequest();
promise2 = baiduRequest();
promise3 = taobaoRequest();
all([promise1,promise2,promise3]).then(function(result){
handleRequest();
});
doOtherWorks();
}
Future
Future是多线程计算的一种阻塞模式.假设有两个线程T1, T2. 它们在分别计算两组数据,希望将两个线程分别计算出来的结果相乘,得到最终答案. 存在这么一种模式,使得编程非常直观:
Future1 = T1.compute();
Future2 = T2.compute();
doOtherWorks();
Final = Future1.get()*Future2.get();
非常直观, future1就是存储T1运算结果的地方.同理future2是T2的运算结果.直接调用Future.get()就可以取出结果. T1和T2在运行开始的时候,约定好计算结束的时候把结果放入Future1和Future2.然后就各自去执行计算了,主线程也并不需要等待T1和T2的返回,可以继续执行doOtherWorks(). 只有当调用Future.get()的时候,如果T还没计算完成,那么get()就会阻塞.直到T把结果存储到Future中.
JAVA是如何实现Future模式的?
Java中存在Runnable, Callable, Future接口, FutureTask类. Callable和Runnable相同的作用,Callable使得线程执行call()方法,并将值返回给Future. Future只是一个存储call()值的地方. FutureTask是一个实现了Runnable和Future接口的类, 使用new FutureTask(Callable call)生成一个task线程, 调用task.start()启动线程, 线程调用Callable的call方法,并将值存于task中(task实现了Future). 后面直接调用task.get()来获得线程计算值.
下面给出一个可以运行的JAVA实例. 主线程发起两个支线程,分别计算各自的数值.主线程将两个线程的计算结果相乘,计算最终数值.
package concurrency;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
int result = new FutureTest().test();
System.out.println(result);
}
private int test() throws InterruptedException, ExecutionException {
MyCall call1 = new MyCall("1");
FutureTask task1 = new FutureTask(call1);
new Thread(task1).start();
MyCall call2 = new MyCall("2");
FutureTask task2 = new FutureTask(call2);
new Thread(task2).start();
return task1.get()*task2.get();
}
private class MyCall implements Callable {
private String name;
private MyCall(String name) {
this.name = name;
}
@Override
public Integer call() throws Exception {
int i=10;
while (i>0) {
i--;
System.out.println("MyCall:["+name+"] is working.");
Thread.sleep(1000);
}
return Integer.valueOf(12);
}
}
}