JAVA并发编程(八)——Callable和Future

本文介绍了多线程开发中常用的Future模式,通过示例解释了如何利用该模式实现异步调用,提高程序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Future模式

在多线程开发中,Future模式是经常使用到的一种设计模式,其核心思想就是异步调用。当我们要调用一个函数方法fun时,fun的执行很慢,如果我们不需要着急知道fun的结果时,我们可以让调用者直接返回去做其他的任务,让fun在后台继续执行。当我们需要用到fun的结果时再去取。

Future模式就好像我们在网上购物,我们只要负责下单就可以做自己的事情,而不需要傻傻的等待商品到来。我们只需要在商品送货上门到来的时候提供购买凭证即可。流程图如下:

这里写图片描述

客户端发起一次请求给服务端,虽然服务端执行这次请求需要花费很久,但是它会立即返回一个假的结果给客户端,此时客户端没有着急去处理返回的结果,而是去做了其他的事情;再完成了其他任务时再去处理返回的结果,而此时服务端已经能返回真实的结果了。

interface Data {
    public String getData();
}

class FutureData implements Data {
    RealData realData = null;//包装了RealData
    boolean isReady = false;

    public synchronized void setData(RealData realData) {
        if (isReady) {
            return;
        }
        this.realData = realData;
        isReady = true;
        notifyAll();//通知getData已经注入realData
    }

    @Override
    public synchronized String getData() {
        while (!isReady) {//realData没有被注入被一直等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return realData.result;
    }
}

class RealData implements Data {
    final String result;

    public RealData(String para) {
        StringBuffer sb = new StringBuffer();
        //构造数据中
        for (int i = 0; i < 3; i++) {
            sb.append(para);
            try {
                //表示构造数据很漫长
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        result = sb.toString();
    }

    @Override
    public String getData() {
        return result;
    }

}

public class Client {

    public Data request(final String para) {
        final FutureData future = new FutureData();
        new Thread() {
            public void run() {
                //RealData构建很慢
                RealData realData = new RealData(para);
                future.setData(realData);
            }
        }.start();
        return future;//FutureData马上被返回
    }

    public static void main(String[] args) {
        Client client = new Client();
        Data data = client.request("name");//马上返回一个假的data
        System.out.println("请求完毕,时间:"+System.currentTimeMillis()/1000);
        //System.out.println("futuredata:"+data.getData()+",时间:"+System.currentTimeMillis()/1000);
        try {
            TimeUnit.SECONDS.sleep(7);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //返回真实的data
        System.out.println("realdata:"+data.getData()+",时间:"+System.currentTimeMillis()/1000);
    }
}
/*
请求完毕,时间:1508380112
realdata:namenamename,时间:1508380119

如果把注释去掉:
请求完毕,时间:1508380253
futuredata:namenamename,时间:1508380259
realdata:namenamename,时间:1508380266
*/

由输出可以知道,如果需要马上对数据进行处理,则会造成线程等待。


JDK中的Future模式

JDK中的Future模式的基本结构如下所示:

这里写图片描述

我们需要关注的是RunnableFuture,它实现了Future接口(就像上文描述的一个订单,通过它可以获得数据)和Runnable接口,FutureTask是具体的实现,get用于获得真实数据,run用于真实数据构造;他会委托Sync类实现,而Sync类回调用Callable接口实现。Callable接口只有一个call方法,它会返回我们需要构造的实际数据。

class RealData implements Callable<String> {
    private String para;

    public RealData(String para) {
        this.para = para;
    }
    @Override
    public String call() throws Exception {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 3; i++) {
            sb.append(para);
            TimeUnit.SECONDS.sleep(1);
            //System.out.println(sb);
        }
        return sb.toString();
    }
    public class FutureMain {
    }
    public static void main(String[] args) throws InterruptedException,
            ExecutionException {
        //构造FutureTask
        FutureTask<String> futureTask = new FutureTask<>(new RealData("a"));
        ExecutorService service = Executors.newFixedThreadPool(1);
        //执行FutureTask相当于上例中request("name")
        service.execute(futureTask);
        System.out.println("请求提交");
        long start = System.currentTimeMillis();
        try {
            //模拟主线程做了一下其他的事情
            System.out.println("做了其他的事情");
            TimeUnit.SECONDS.sleep(5);
            System.out.println("其他的事情做完了");     
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("结果为" + futureTask.get());
        long end = System.currentTimeMillis();
        System.out.println("用时:" + (end - start) / 1000);
    }
}
/*
请求提交
做了其他的事情
其他的事情做完了
结果为aaa
用时:5
*/

从程序用时可以看出,主线程和Future之间分工明确,异步执行。

除了基本功能外,Future还提供了一下简单的控制方法:

//取消任务
boolean cancel(boolean mayInterruptIfRunning);
//任务是否取消
boolean isCancelled();
//任务是否完成
boolean isDone();
//取得返回对象
V get() throws InterruptedException,ExecutionException;
//取得返回对象,设置超时时间
get(long timeout,TimeUnit unit);

如果对未取得真实数据的data使用get会使得线程等待,可以为get设置一个时间或者使用isDone来判断data是否取得真实数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值