java多线程:Future模式

Request-Per-Task每个请求一个任务可以实现异步任务,但是不能获取任务的结果,Future模式可以获取异步任务的结果

独立开线程:使用FutureTask或者线程条件(if balk while wait)实现Future模式

一个Data的接口

package com.FutureExample;

public interface Data {
    public abstract String getContent();
}

RealData实现Data,构造方法延时产生真正数据,包含获取真正的数据getContent方法

package com.FutureExample;

public class RealData implements Data {
    private final String content;

    public RealData(int count,char c){
        System.out.println("构造产生真实数据需要一段时间:"+count+"个"+c+"开始");
        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("构造产生真实数据需要一段时间:"+count+"个"+c+"结束");
        this.content=new String(buffer);// 初始化
    }

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

FutrueData实现RealData,使用if balk 和while wait模式设置和获得数据

package com.FutureExample;

public class FutureData implements Data {

    private RealData realData=null;
    private boolean dataReadyFlag=false;// 数据是否准备完成
    public synchronized void setRealData(RealData realData){
        if(dataReadyFlag){// 数据准备完成直接走开
            return;
        }

        this.realData=realData;
        dataReadyFlag=true;
        notifyAll();
    }

    @Override
    public synchronized String getContent() {
        while (!dataReadyFlag){
            try {
                wait();
            }catch (InterruptedException e){}
        }

        return realData.getContent();
    }
}

使用FutureTask实现的FutureData,不需要考虑细节,只需要传入Callable接口,调用FutureTask的get方法就能获取Callable的返回对象(RealData)

首先考察FutureTask 实现了Runnable接口,可以使用new Thread(FutureTask obj).start() 。调用FutureTask中的run方法,实际上是运行其中的run方法,可以看出来run方法中调用了Callable接口中的call方法,将结果设置成result。

FutureTask中只有有参构造方法,所以子类必须含有参构造方法而且必须显式调用父类构造方法。

public FutureTask(Callable<V> callable) {
   	if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}
package com.FutureExample;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class FutureDataBetter extends FutureTask<RealData> implements Data {

    public FutureDataBetter(Callable<RealData> callable){
    	// FutureTask调用Callable的call方法的结果设置成result。
        super(callable);// 因为先有父类才能有子类,父类只有有参构造方法,子类必须显式的调用,才能构造出父类,(而且只能在子类的有参构造函数中调用,无参的构造函数中默认有一个super()可以调用父类的无惨构造函数,但是有参必须显示调用。)
    }

    @Override
    public String getContent() {
        String str=null;

        try{
            str=super.get().getContent();// super.get()获得的是RealData,realdata构造需要一段时间延时获取
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        return str;
    }
}

class FutureDataBetter extends FutureTask。FutureDataBetter继承了FutureTask,FutureTask没有无参构造方法,只有有参构造方法,所以在FutureDataBetter中必须同样存在有参构造函数,并在构造函数中显示的调用调用父类的有参构造函数-super(callable)。否则会报在父类构造函数调用前是不能引用Callable对象的&父类中不存在默认的构造函数(无参构造函数)。
Host类:用来发出请求将来的数据(FutureData)

package com.FutureExample;

import java.util.concurrent.Callable;

public class Host {
    public Data requstFutrueData(final int count,final char c){
        System.out.println("请求futrueData开始"+count+c);

//        final FutureData futureData=new FutureData();
//
//         new Thread(){
//             public void run(){
//                 RealData realData=new RealData(count, c);
//                 futureData.setRealData(realData);
//             }
//         }.start();

         FutureDataBetter futureDataBetter=new FutureDataBetter(new Callable<RealData>() {
             @Override
             public RealData call() throws Exception {
                 return new RealData(count, c);// new对象需要时间
             }
         });
         // 开线程:调用Callable的call方法的结果设置成result
        new Thread(futureDataBetter).start();
        System.out.println("请求futrueData结束"+count+c);
//        return futureData;
        return futureDataBetter;
    }
}

使用线程池,阻塞获取get现Future

这种模式通过线程池实现。更简单,省略了Host手动开线程请求数据。但这种方法会阻塞主线程。
get():获取结果(可能会等待)
get(long timeout, TimeUnit unit):获取结果,但只等待指定的时间;
cancel(boolean mayInterruptIfRunning):取消当前任务;
isDone():判断任务是否已完成。

package com.FutureExample;

import java.util.concurrent.Callable;

public class CallableTask implements Callable<String> {

    private RealData realData;

    public CallableTask(int count,char c){
        this.realData=new RealData(count, c);
    }

    @Override
    public String call() throws Exception {
        return this.realData.getContent();
    }
}

通过源码可以看到 Future futureData= executorService.submit(callableTask);
submit提交了一个Callable任务,这个Callable返回结果的最终实现是FurtureTask调用了run方法进而调用了call方法,把结果set成result。不用开线程实现,线程池实现代替了Host开线程的实现。

ComfortableFuture异步回调

底层也是线程池不过不会阻塞主线程,使用回调的方式执行
异步任务结束时,会自动回调某个对象的方法;
异步任务出错时,会自动回调某个对象的方法;
主线程设置好回调后,不再关心异步任务的执行。
但是主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭

串行调用

public class Main {
    public static void main(String[] args) throws Exception {
        // 第一个任务:
        CompletableFuture<String> cfQuery = CompletableFuture.supplyAsync(() -> {
            return queryCode("中国石油");
        });
        // cfQuery成功后继续执行下一个任务:
        CompletableFuture<Double> cfFetch = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice(code);
        });
        // cfFetch成功后打印结果:
        cfFetch.thenAccept((result) -> {
            System.out.println("price: " + result);
        });
        // 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
        Thread.sleep(2000);
    }

    static String queryCode(String name) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        return "601857";
    }

    static Double fetchPrice(String code) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        return 5 + Math.random() * 20;
    }
}

并行使用

public class Main {
    public static void main(String[] args) throws Exception {
        // 两个CompletableFuture执行异步查询:
        CompletableFuture<String> cfQueryFromSina = CompletableFuture.supplyAsync(() -> {
            return queryCode("中国石油", "https://finance.sina.com.cn/code/");
        });
        CompletableFuture<String> cfQueryFrom163 = CompletableFuture.supplyAsync(() -> {
            return queryCode("中国石油", "https://money.163.com/code/");
        });

        // 用anyOf合并为一个新的CompletableFuture:
        CompletableFuture<Object> cfQuery = CompletableFuture.anyOf(cfQueryFromSina, cfQueryFrom163);

        // 两个CompletableFuture执行异步查询:
        CompletableFuture<Double> cfFetchFromSina = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice((String) code, "https://finance.sina.com.cn/price/");
        });
        CompletableFuture<Double> cfFetchFrom163 = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice((String) code, "https://money.163.com/price/");
        });

        // 用anyOf合并为一个新的CompletableFuture:
        CompletableFuture<Object> cfFetch = CompletableFuture.anyOf(cfFetchFromSina, cfFetchFrom163);

        // 最终结果:
        cfFetch.thenAccept((result) -> {
            System.out.println("price: " + result);
        });
        // 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
        Thread.sleep(200);
    }

    static String queryCode(String name, String url) {
        System.out.println("query code from " + url + "...");
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
        }
        return "601857";
    }

    static Double fetchPrice(String code, String url) {
        System.out.println("query price from " + url + "...");
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
        }
        return 5 + Math.random() * 20;
    }
}

anyOf()可以实现“任意个CompletableFuture只要一个成功”,allOf()可以实现“所有CompletableFuture都必须成功”,这些组合操作可以实现非常复杂的异步流程控制。
最后我们注意CompletableFuture的命名规则:
xxx():表示该方法将继续在已有的线程中执行;
xxxAsync():表示将异步在线程池中执行。

thenAccept()处理正常结果;
exceptional()处理异常结果;
thenApplyAsync()用于串行化另一个CompletableFuture;
anyOf()和allOf()用于并行化多个CompletableFuture。

Main方法

package com.FutureExample;

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException, ClassNotFoundException {
        System.out.println("主线程开启");

        Host host=new Host();
        Data data1=host.requstFutrueData(10, 'a');
        Data data2=host.requstFutrueData(20, 'b');
        Data data3=host.requstFutrueData(30, 'c');
        System.out.println("主线程其他任务开启");

        // 线程池Future
        ExecutorService executorService= Executors.newCachedThreadPool();
        CallableTask callableTask=new CallableTask(40, 'd');


        Future<String> futureData= executorService.submit(callableTask);
        // comfortable future
        RealData realData=new RealData(50, 'e');
        CompletableFuture completableFuture=CompletableFuture.supplyAsync(realData::getContent);

        try {
            Thread.sleep(500);   // 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
        }catch (InterruptedException e){}

        System.out.println("data1:"+data1.getContent());
        System.out.println("data2:"+data2.getContent());
        System.out.println("data3:"+data3.getContent());
        System.out.println("futureData:"+futureData.get());

            completableFuture.thenAccept((result)->{
           System.out.println("comfatable future:"+result);
        });
        System.out.println("主线程结束");
    }
}


结果: 主线程开启
请求futrueData开始10a 请求futrueData结束10a
请求futrueData开始20b 请求futrueData结束20b
请求futrueData开始30c 请求futrueData结束30c
主线程其他任务开启
构造RealData对象需要一段时间:10个a开始 构造RealData对象需要一段时间:30个c开始
构造RealData对象需要一段时间:20个b开始 构造RealData对象需要一段时间:40个d开始
构造RealData对象需要一段时间:10个a结束 构造RealData对象需要一段时间:20个b结束
构造RealData对象需要一段时间:30个c结束 构造RealData对象需要一段时间:40个d结束
构造RealData对象需要一段时间:50个e开始 构造RealData对象需要一段时间:50个e结束
data1:aaaaaaaaaa
data2:bbbbbbbbbbbbbbbbbbbb
data3:cccccccccccccccccccccccccccccc
futureData:dddddddddddddddddddddddddddddddddddddddd
comfatable
future:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
主线程结束

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值