Java并发之Future模式

本篇是《图解Java多线程设计模式》第九章的读书笔记。

Future的意思是未来,假设有一个方法需要花费很长的时间才能获取到运行结果,那么与其一直等待不如先去忙别的,等你完成我再来拿。

来看看示例代码:
在这里插入图片描述
先来看下各个类之间的关系:
在这里插入图片描述

Data接口

RealDataFutureData 实现的接口

public interface Data {
    String getContent();
}

RealData :代表需要花费很长时间的任务

/**
 * RealData 代表需要花费很长时间的任务
 */
public class RealData implements Data {

    private final String content;

    public RealData(int count, char c) {
        System.out.println("        making RealData("+count+", "+c+") BEGIN");
        char[] buffer = new char[count];
        for (int i = 0; i < count; i++) {
            buffer[i] = c;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("        making RealData("+count+", "+c+") END");
        this.content = new String(buffer);
    }

    @Override
    public String getContent() {
        return content;
    }
}

关于上面代码中为什么 content 要用 final 修饰 ?这与 final 关键字的语义有关。
JSR-133增强了 final 的语义,为Java程序员提供初始化安全保证:只要对象是正确构造的(构造器没有逸出),那么不需要使用同步就可以保证任意线程都能看到这个final域在构造函数中被初始化之后的值。

FutureData : main 线程得到的返回就是它,通过调用它的 getContent 方法获取任务执行结果,辅助线程会在任务执行完后调用 FutureData 的 setRealData 方法,传进任务对象 RealData。

public class FutureData implements Data{
    private RealData realData;
    private boolean ready;

    public synchronized void setRealData(RealData realData) {
        if (ready) {
            return;
        }
        this.realData = realData;
        this.ready = true;
        notifyAll(); // 任务已完成,唤醒所有等待的获取任务结果线程
    }

    @Override
    public synchronized String getContent() {
        while (!ready) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        return realData.getContent();
    }
}

Host : 创建新线程,由新线程来完成耗时任务,将 Future 返回给 Client,新线程执行完任务后将将任务对象设置到 RealData 中。

public class Host {

    public Data request(final int count, final char c) {
        System.out.println("    request("+count+", "+c+") BEGIN");
        final FutureData futureData = new FutureData(); // 创建FutureData的实例
        // 启动一个线程执行耗时任务
        new Thread(() -> {
            RealData realData = new RealData(count, c);
            futureData.setRealData(realData);
        }).start();
        System.out.println("    request("+count+", "+c+") END");
        return futureData; // 返回FutureData
    }
}

该类是线程安全的,因为它不带有任何状态。

Main : 代表 Client,请求者的角色,想 Host 发出请求,并立即收到请求的返回,也就是 Future,客户端可以先去干其他事,之后再获取结果。

public class Main {
    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(15,'B');
        Data data3 = host.request(20,'C');

        System.out.println("main other jobs BEGIN");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main other jobs END");

        System.out.println("data1 = " + data1.getContent());
        System.out.println("data2 = " + data2.getContent());
        System.out.println("data3 = " + data3.getContent());

        System.out.println("main END");
    }
}

重点在于任务结果被辅助线程设置回 Future 中。

来看看执行结果:

main BEGIN
    request(10, A) BEGIN
    request(10, A) END
    request(15, B) BEGIN
    request(15, B) END
    request(20, C) BEGIN
    request(20, C) END
main other jobs BEGIN
        making RealData(10, A) BEGIN
        making RealData(20, C) BEGIN
        making RealData(15, B) BEGIN
        making RealData(10, A) END
        making RealData(15, B) END
main other jobs END
data1 = AAAAAAAAAA
data2 = BBBBBBBBBBBBBBB
        making RealData(20, C) END
data3 = CCCCCCCCCCCCCCCCCCCC
main END

类的时序图:
在这里插入图片描述

JUC

java.util.concurrent.Future 接口相当于上面的 Future 角色,FutureTask 类实现了该接口。
在这里插入图片描述
Callable 对象用来构造 FutureTask 对象,FutureTask 的 run 方法调用 Callable 的 call 方法,辅助线程获取到 call 发返回值,然后通过 FutureTask 的 set 方法来设置返回值,若发生异常则调用 setException 设置异常。这样 Client 就可以通过 get 得到结果。

利用 FutureTask 来更改上面的例子:

FutureData

public class FutureData extends FutureTask<RealData> implements Data{

	public FutureTask(Callable<RealData> callable) {
		super(callable);
	}
    @Override
    public String getContent() {
        String res = null;
        try{
			res = get().getContent();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}  catch (ExecutionException e) {
			e.printStackTrace()
;		}
        return res;
    }
}

Host

public class Host {

    public Data request(final int count, final char c) {
        System.out.println("    request("+count+", "+c+") BEGIN");
        FutureData future = new FutureData(
			new Callable<RealData>() {
				public RealData call() {
					return new RealData(count, c);
				}
			}
		);
		new Thread(future).start();
        System.out.println("    request("+count+", "+c+") END");
        return future; // 返回FutureData
    }
}

在这里插入图片描述

关于 FutureTask 的源码分析,见我的这一篇
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值