多线程基础之设计模式Future模式

  一. Future模式介绍

       前面的 Thread-Per-Message 模式将耗时时间的处理交给其他线程, 的确可以提高程序的响应性, 但是在将处理交出去的时候, 处理结果仍是未知的, 而等待处理结果的话, 线程的响应性就会降低, 这时就可以使用 Future 模式

        假设我们去蛋糕店买蛋糕, 下单时, 店员一遍递给我们提货单, 一边说 "请你傍晚再来取蛋糕", 到了傍晚, 我们就可以拿着提货单去取蛋糕, Futrue 就是未来的意思, 假设有一个方法需要花费很长时间才能获取到运行结果, 那么, 与其一直等待结果, 不如先拿一张 "提货单", 获取提货单并不耗费时间, 这里的 "提货单" 我们就称为 Future 角色     

        获取 Future 角色的线程会在稍后使用 Future 角色来获取运行结果, 这与凭借着提货单去取蛋糕非常相似, 如果运行结果已经出来了, 那么直接领取就可以, 如果运行结果没出来, 就需要等待结果出来, 建议先看下 Thread-Per-Message 模式 多线程基础之设计模式Thread-Per-Message模式_canxiusi的博客-CSDN博客

二. 示例程序

类名说明
Main测试程序类
Host向请求返回FutureData的实例的类
Data表示访问数据的方法的接口, 有FutureData和RealData实现该接口
FutureData表示RealData的 "提货单" 类, 其他线程会创建RealData的实例
RealData表示实际数据的类, 构造函数的处理会花费很长时间

2.1 Main类

        Main 类会调用 request() 方法三次, 接着他会接收三个 Data 实例作为返回值, 实际上都是 FutureData 的实例, 无需花费时间即可获取他们, 类似蛋糕的提货单

/**
 * @author canxiusi.yan
 * @description Main
 * @date 2022/6/6 22:11
 */
public class Main {
    public static void main(String[] args) {
        System.out.println("main begin");
        Host host = new Host();
        Data futureData1 = host.request(10, 'a');
        Data futureData2 = host.request(20, 'b');
        Data futureData3 = host.request(30, 'c');

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            //
        }
        System.out.println("futureData1.getContent() = " + futureData1.getContent());
        System.out.println("futureData2.getContent() = " + futureData2.getContent());
        System.out.println("futureData3.getContent() = " + futureData3.getContent());
        System.out.println("main end");
    }
}

2.2 Host类

        会创建 FutureData 的实例, 启动一个新线程, 用于创建 ReadData 的实例, 将 FutureData 的实例作为返回值返回给调用者, 如直接调用 RealData 的实例相比, 这三项操作的处理事件都非常短, 因此, 调用 request 方法的线程可以很快从方法中返回

/**
 * @author canxiusi.yan
 * @description Host
 * @date 2022/6/6 22:11
 */
public class Host {
    public Data request(final int count, final char c) {
        System.out.println("   request(" + count + ", " + c + ")  begin");
        final FutureData futureData = new FutureData();
        // 这里模拟程序, 就先显式创建线程池
        new Thread(() -> {
            RealData realData = new RealData(count, c);
            futureData.setRealData(realData);
        }).start();
        return futureData;
    }
}

2.3 Data接口

       FutureData 和 RealData 实现了该接口

/**
 * @author canxiusi.yan
 * @description Data
 * @date 2022/6/6 22:12
 */
public interface Data {
    abstract String getContent();
}

2.4 FutureData类

        FutureData 类表示 "提货单" 的类, realData 字段是用于保存稍后创建完毕的 ReadData 的实例的字段, 我们可以通过 setReadData 方法设置该字段

        ready 字段表示是否已经为 readData 赋值的字段, 为 true 时, 表示蛋糕制作完成了

/**
 * @author canxiusi.yan
 * @description FutureData
 * @date 2022/6/6 22:12
 */
public class FutureData implements Data {

    private RealData realData;
    private boolean ready = false;

    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();
    }
}

2.5 ReadData类        

        ReadData 类是一个需要花费很长时间才能创建好的实例类, Future 模式就是为了这样的 "需要花费很长时间进行处理的普通的类" 生成一张 "提货单", 并通过多线程来提高程序性能的模式

/**
 * @author canxiusi.yan
 * @description RealData
 * @date 2022/6/6 22:12
 */
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) {
                //
            }
        }
        System.out.println(" making RealData(" + count + ", " + c + ")  end");
        this.content = new String(buffer);
    }

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

2.6 运行示例

        从图中可以看出, 3次request() 方法就立即停止了, 这与 Thread-Per-Message 模式运行结果相同, RealData 的实例是在新线程中创建的, 在主线程进行其他处理的同时, RealData 的实例也在被慢慢创建, 接着主线程会调用 getContent() 方法, 如果 RealData 实例创建好了, 程序会立即打印内容, 如果还没创建好, 则会继续等待

main begin
   request(10, a)  begin
   request(20, b)  begin
   request(30, c)  begin
 making RealData(10, a)  begin
 making RealData(30, c)  begin
 making RealData(20, b)  begin
 making RealData(10, a)  end
futureData1.getContent() = aaaaaaaaaa
 making RealData(20, b)  end
futureData2.getContent() = bbbbbbbbbbbbbbbbbbbb
 making RealData(30, c)  end
futureData3.getContent() = cccccccccccccccccccccccccccccc
main end

三. Future模式中的登场的角色

3.1 Client(请求者)

        Client 想 Host 发出请求, 并会立即接收到请求的处理结果(返回值)

3.2 Host

        Host 角色会创建新的线程, 并开始在新的线程中创建 RealData, 同时 他会将 Future 角色返回给 Client 角色, 在实例程序中, 有 Host 类扮演此角色

3.3 VirtualData(虚拟数据)

        VirtualData 角色能让 Future 角色与 RealData 角色具有一致性, 在示例程序中, 由 Data 扮演

3.4 ReadData(真实数据)

        RealData 角色表示真是数据的角色, 创建该对象需要花费很长的时间, 在示例程序中, 由  ReadData 类扮演该角色

3.5 Future(期货)       

        Future 是 RealData 的 "提货单", 由 Host 角色传递给 Client, 从程序行为上看, 对 Client 而言, Future 角色就是 VirtualData

3.5 模式类图

 四. java.util.concurrent包和Future模式

       下面使用 java.util.concurrent 包来改写前面的示例程序, 需要修改的只有 Host 类和 FutureData 类

4.1 通过java.util.concurrent包改造Host类

        这里传递给 FutureData 的构造函数的参数是 Callable 对象, 他的 call() 方法处理的是 "创建新的 RealData 的实例并返回给调用者, (1) 中只是创建了 FutureData 的实例, 创建 RealData 的实例的实际处理还没开始, (2) 中创建并启动一个线程, 用于真正的创建 RealData 的实例, 这里启动的新线程会调用 Callable 的 call() 方法, 然后 set 值

/**
 * @author canxiusi.yan
 * @description Host
 * @date 2022/6/6 22:11
 */
public class Host {
    public Data request(final int count, final char c) {
        System.out.println("   request(" + count + ", " + c + ")  begin");
        // (1)
        final FutureData futureData = new FutureData(new Callable<RealData>() {
            @Override
            public RealData call() throws Exception {
                return new RealData(count, c);
            }
        });
        // (2)
        new Thread(futureData).start();
        return futureData;
    }
}

4.2 通过java.util.concurrent包改造FutureData类

        FutureData 类继承了 java.util.concurrent.FutureTask 类, 构造函数接收到返回值 Callable 对象会直接传递给父类 FutureTask, 由于 call() 方法的调用被交给了 FutureTask 类, 所以也无需在 FutureData 中编写相关的代码 

/**
 * @author canxiusi.yan
 * @description FutureData
 * @date 2022/6/6 22:12
 */
public class FutureData extends FutureTask<RealData> implements Data {

    public FutureData(Callable<RealData> callable) {
        super(callable);
    }

    @Override
    public String getContent() {
        try {
            return get().getContent();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return null;
    }
}

4.3 使用了java.util.concurrent包以后的程序时序图

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值