future设计模式学习

1.Future设计模式:

1).定义了一个接口Data,这是客户端希望获取的数据,这个接口有两个实现分别是RealData和FutureData,RealData是真实的数据,FutureData是用来在真实数据构建完后获取它的。

2).在提出一个请求后,会立即返回一个FutureData类型的数据futureData,这里注意此时futureData中并没有真实值,此时如果使用futureData.getResult()获取数据,此时线程会阻塞。而Future模式期待的并不是这种情况,而是在发出请求后,线程去做别的事务,在经历了一段时间后,再从futureData中提取出真实数据

3)正常的流程如下:

RealData和FutureData都实现了Data接口

主线程发出请求后,立即得到一个futureData数据,同时主线程开启一个新的线程

主线程去做别的事务—此时futureData数据并不能被使用,如果此时要理解获取futureData中的数据将等待

新的线程构造真实数据RealData,在RealData的构造函数中将获取请求的结果,并保存在RealData的私有属性中,然后调用FutureData的setRealData(RealData rd)方法,返回获取请求的结果,并唤醒可能阻塞的方法getResult()

主线程使用到futureData数据,futureData使用getResult()方法获取真实数据

注意点

RealData和FutureData都实现了Data接口

RealData构建真实结果

FutureData只有RealData的引用,同时在FutureData中获取getResult()方法,使用了等待唤醒的方式,在数据没有准备好的时候,获取真实数据将进入等待。

示例代码:
interface Data {
    public String getResult();

}

//实现了一个快速返回的RealData包装
class FutureData implements Data {

    protected RealData realData = null;		//成员变量就是真实数据ReadData
    protected boolean isReady = false;		//数据准备好后设置为true

    public synchronized void setRealData(RealData realData) {	//等待RealData准备了
        if (isReady) {
            return;
        }
        this.realData = realData;		//获取真实的数据
        isReady = true;
        notify();					//RealData已经被注入,通知getResult
    }


    @Override
    public synchronized String getResult() {		//如果数据没有准备好,会进入阻塞
        while (!isReady) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return realData.result;		//获取了处理后的数据
    }
}

class RealData implements Data{

    protected final String result;

    public RealData(String param) {
        //RealData的构造很慢,需要用户等待很久,这里用sleep模拟
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < 10; i++) {
            stringBuffer.append(param);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        result = stringBuffer.toString();		//处理后的数据
    }
    
    public String getResult() {
        return result;
    }
}


class Client {
    public Data request(final String queryStr) {
        final FutureData futureData = new FutureData();
        //开启了一个新的线程
        new Thread() {
            public void run() {//RealData的构造很慢,所以在单独的线程中运行
                RealData realData = new RealData(queryStr);	//耗时的操作
                futureData.setRealData(realData);
            }
        }.start();
        return futureData;	//这是一个假数据
    }
}


public class TestFuture {
    public static void main(String args[]) {
        Client client = new Client();
        //这里会立即返回,因为得到的是FutureData而不是RealData
        Data data = client.request("name");
        System.out.println("请求完毕");
        System.out.println("返回一个假数据: "+data);
        System.out.println("去处理别的事情");
        try {

            //这里用一个sleep代替了对其他业务逻辑的处理
            //在处理这些业务逻辑的过程中,RealData被创建,从而充分利用了等待时间
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //使用真实的数据
        System.out.println("数据=" + data.getResult());
    }
}

2.FutureTask学习

如果开启的新线程执行完成后,要返回结果,这时候就可以考虑使用实现Callable接口而不是Runnable接口。最常见的场景是如果开启一个新线程去执行计算(因为计算可能花去较多时间),计算的结果也许不是立即使用,这时候可以使用future模式。

FutureTask常见使用示例代码:

class CallableDemo implements Callable<Integer> {//泛型,Integer是线程执行返回结果
    
    private int sum;  
    @Override  
    public Integer call() throws Exception {  
        System.out.println("Callable子线程开始计算啦!");  
        Thread.sleep(2000);  
          
        for(int i=0 ;i<5000;i++){  
            sum=sum+i;  
        }  
        System.out.println("Callable子线程计算结束!");  
        return sum;  
    }  
} 


public class TestFuture{
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService es = Executors.newFixedThreadPool(1);
		FutureTask<Integer> task = new FutureTask<>(new CallableDemo());
		es.submit(task);	//FutureTask实现了Runnble接口和Future接口
		es.shutdown();
		System.out.println("主线程继续执行别的任务");
		System.out.println("计算执行结果:"+task.get());
	}
}


Future常见使用示例代码:

class CallableDemo implements Callable<Integer> {//泛型,Integer是线程执行返回结果
    
    private int sum;  
    @Override  
    public Integer call() throws Exception {  
        System.out.println("Callable子线程开始计算啦!");  
        Thread.sleep(2000);  
          
        for(int i=0 ;i<5000;i++){  
            sum=sum+i;  
        }  
        System.out.println("Callable子线程计算结束!");  
        return sum;  
    }  
} 


public class TestFuture{
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService es = Executors.newFixedThreadPool(1);
		Future<Integer> future = es.submit(new CallableDemo());
		System.out.println("主线程继续执行别的任务");
		System.out.println("计算执行结果:"+future.get());
	}
}

参考博客:

https://www.cnblogs.com/dolphin0520/p/3949310.html

https://blog.csdn.net/javazejian/article/details/50896505

小结:

1)使用Callable接口创建线程可以获取返回值

2)FutureTask实现了Runnable接口和Future接口,因为FutureTask实现了Runnable接口,所以FutureTask可以交给Executor执行

3)Future接口中常见的方法是get()方法,用来获取Callable接口的返回值

4)应用场景:我们需要计算一个数,而这个数的计算很耗时,所以我们可以创建一个新线程去执行计算,而主线程继续做其他事情,而后面用到这个数据的时候,我们使用Future或者FutureTask来获取就可以了。

5)FutureTask有3中状态:

未启动,已启动,已完成   只有已完成状态下使用get()方法才返回结果,未启动和已启动状态调用get()方法会阻塞。



猜你喜欢

转载自blog.csdn.net/chenkaibsw/article/details/80475200