一、Future简介
为什么需要future模式?
正常客户访问服务器的时候会打开一个线程,该线程按顺序运行任务,也就是客户端首先获取反馈,只有反馈之后客户端才能进行后序的执行,但是如果有时候是获取比较大的资源呢?用户由于必须要给出反馈,因此会一直在一个页面停顿,这种与用户交互非常不合理。
如果是选择异步的方式交互,也就是通过future模式,用户请求的时候先给用户一个反馈,用户拿到一个凭证,服务器端在慢慢处理得到结果,用户在拿到凭证之后就可以执行之后的任务,直到真正需要该结果通过凭证拿到结果。
例子
1、Host作为客户访问,首先通过Futuredata返回一个结果
2、Host的访问会新建一个线程,产生RealData
3、Host真正需要结果的时候,也就是getContent返回结果
注意:FuturData选择等待通知模式
interface Data {
public abstract String getContent();
}
/**
* 真实构建数据
*
*/
class RealData implements Data {
private String content;
public RealData(int count, char c) {
System.out.println("real data[" + count + ":" + c + "]start");
char[] res = new char[count];
for (int i = 0; i < res.length; i++) {
res[i] = c;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("real data[" + count + ":" + c + "]end");
content = String.valueOf(res);
}
@Override
public String getContent() {
return content;
}
}
class FuturData implements Data {
private RealData real = null;
private boolean ready = false;
public synchronized void setContent(RealData real) {
while(ready) {
return ;
}
this.real = real;
ready = true;
notifyAll();
}
@Override
public synchronized String getContent() {
while(!ready) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return real.getContent();
}
}
class Host {
public Data request(int count, char c) {
System.out.println("request data[" + count + ":" + c + "]start");
final FuturData future = new FuturData();
new Thread(){
@Override
public void run() {
RealData real = new RealData(count, c);
future.setContent(real);
}
}.start();//必须开启线程
System.out.println("request data[" + count + ":" + c + "]end");
return future;
}
}
public class FutureDemo {
public static void main(String[] args) {
System.out.println("main BEGIN");
Host host = new Host();
Data data1 = host.request(10, 'A');
Data data2 = host.request(20, 'B');
Data data3 = host.request(10, 'C');
System.out.println("main END");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("other job BEGIN");
System.out.println(data1.getContent());
System.out.println(data2.getContent());
System.out.println(data3.getContent());
System.out.println("other job END");
}
}
二、FutureTask
需要提到的是由于1.8对1.7大部分内容进行了大刀阔斧的修改,1.7中FutureTask使用AQS(同步器实现),1.8中使用Unsafe类来实现
1、 类图
2、属性
private volatile int state;
private static final int NEW = 0;//初始化状态,所有操作保证在NEW状态之后
private static final int COMPLETING = 1;//准完成状态,就是任务完成了,结果还没设置,中间状态
private static final int NORMAL = 2;//正常结束
private static final int EXCEPTIONAL = 3;//报错结束
private static final int CANCELLED = 4;//取消状态
private static final int INTERRUPTING = 5;//准中断状态,正在往中断状态过度(中间状态)
private static final int INTERRUPTED = 6;//中断状态
3、内部静态类
用来存放等待线程
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
4、构造函数
FutureTask(Runnable runnable, V result)
FutureTask(Callable<V> callable)
一个是通过Callable一个是通过Runnable的方式,会将Runnable任包装成Callable任务
Callable与Runnable区别
1、Runnable采用run方式运行任务,Callable采用call方式运行任务
2、Runnable执行后不能有返回值,Callable执行后有返回值
3、Runnable中run方法出现异常,需要自身处理,Callable中call方法报考异常
4、Callable可以拿到一个任务结果Future对象,通过get()获取结果
5、Runnable可以通过线程启动也可以通过线程池的execute()、submit()启动,Callable只能通过线程池submit()启动
5、任务运行
1、run()
public void run() {
//状态必须是初始化状态
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();//call运行
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);//出错
}
if (ran)
set(result);//设置结果
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);//处理中断
}
}
2、setException():首先将状态修改为COMPLETING,之后修改为EXCEPTIONAL,之后通过finishCompletion释放所有等待线程
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
3、set()设置结果,修改状态到COMPLETING中间状态,之后修改为NORMAL最终状态,最后通过finishCompletion释放所有等待线程
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
runAndReset()可以忽略该方法:主要是从来执行周期任务,在ScheduledFutureTask(可以处理一些定时执行的任务,内部采用DelayQueue队列)会使用,只要是New初始状态就会运行该任务,同时没有设置结果
protected boolean runAndReset() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> c = callable;
if (c != null && s == NEW) {
try {
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
runner = null;
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}
6、任务取消
public boolean cancel(boolean mayInterruptIfRunning) {
//必须为初始状态
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
if (mayInterruptIfRunning) {//中断未执行的线程
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);//修改为中断状态(最终状态)
}
}
} finally {
finishCompletion();
}
return true;
}
7、结果获取
1、get()
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)//未完成状态
s = awaitDone(false, 0L);
return report(s);
//report会根据任务的状态进行映射,如果任务是Normal状态,说明正常执行完成,
//则返回任务结果;如果任务被取消(CANCELLED或INTERRUPTED),则抛出CancellationException;
//其它情况则抛出ExecutionException。
}
2、get(long timeout, TimeUnit unit):增加了等待一段时间后,还未获得结果,抛出异常