Future接口与FutureTask子类

一、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):增加了等待一段时间后,还未获得结果,抛出异常

三、参考

实例引用:https://segmentfault.com/a/1190000015558810

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值