小工具      在线工具  汉语词典  css  js  c++  java

Java多线程 - Future模式

Java 额外说明

收录于:42天前

class FutureTask  {


 private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

/** 任务 */
    private Callable<V> callable;
    /** 储存结果*/
    private Object outcome; // non-volatile, protected by state reads/writes
    /** 执行任务的线程*/
    private volatile Thread runner;
    /** get方法阻塞的线程队列 */
    private volatile WaitNode waiters;
}
   

什么是未来模式

Future模式是多线程开发中非常常见的设计模式。其核心思想是异步调用。当我们需要调用函数方法时。如果这个函数执行得很慢,那么我们就必须等待。但有时,我们可能并不急于得到结果。因此,我们可以让被调用者立即返回,让他在后台慢慢处理请求。对于调用者来说,可以先处理一些其他的任务,然后在真正需要数据的时候再尝试获取需要的数据。

用生活中的例子来说,就像叫外卖一样。例如,我们可以在午休前提前订外卖。我们只需要点餐并下单即可。然后我们就可以继续工作了。中午下班,外卖就到了,然后我就可以吃午饭,美美地睡一觉了。而如果你下班叫外卖,你就只能坐在那里等外卖员了。终于,你拿到外卖,吃完午饭,午休时间也快结束了。

使用Future模式,在获取数据时并不能立即获取到所需的数据。相反,先获得一份合同,以后需要时可以利用这份合同来获取所需的数据。这份合约就像外卖例子中的外卖订单。

使用普通模式和Future模式的区别

我们可以看一下使用普通模式和使用Future模式的时序图。可以看到,普通模式是串行的,遇到耗时操作只能等待。 Future模式只发起一个耗时的操作,函数立即返回,不会阻塞客户端线程。因此,当工作线程执行耗时操作时,客户端不需要等待,可以继续做其他事情,然后在需要时从工作线程获取结果:

1.png

Future模式的简单实现

第一个是FutureData,它只是一个包装类,不需要时间创建。工作线程准备好数据后,就可以使用setData方法传入数据了。客户端线程只需要在需要的时候调用getData方法即可。如果此时数据还没有准备好,getData方法就会等待。如果准备好了就直接返回。

public class FutureData<T> {
    private boolean mIsReady = false;
    private T mData;

    public synchronized void setData(T data) {
        mIsReady = true;
        mData = data;

        notifyAll();
    }

    public synchronized T getData() {
        while (!mIsReady) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return mData;
    }
}

然后是服务器。当客户端向服务器请求数据时,服务器不会真正加载数据。它只是创建一个FutureData,然后创建一个子线程来加载它,并且只需要直接返回FutureData即可。

public class Server {
    public FutureData<String> getString() {
        final FutureData<String> data = new FutureData<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                data.setData("world");
            }
        }).start();

        return data;
    }
}

客户端代码如下。整个程序运行起来只需要2秒多一点,但是如果不使用Future模式的话,需要三秒的时间。

Server server = new Server();
FutureData<String> futureData = server.getString();

//先执行其他操作
String hello = "hello";
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.print(hello + " " + futureData.getData());

JDK 中的未来模式

还记得我之前的文章《Java多线程-线程池》中写的ExecutorService.execute()和ExecutorService.submit()的区别吗(没看过的读者可以看一下)?

execute方法实际上是在Executor中定义的,ExecutorService继承了Executor。它只是提交一个Runnable给线程池中的线程来调用:

public interface Executor {
    void execute(Runnable command);
}

public interface ExecutorService extends Executor {
    ...
}

提交方法是在ExecutorService中定义的,它们都会返回一个Future对象。事实上,submit方法使用的是Future模式:

public interface ExecutorService extends Executor {
    ...
    <T> Future<T> submit(Callable<T> task);
        
    <T> Future<T> submit(Runnable task, T result);
        
    Future<?> submit(Runnable task);
    ...
}

Future<?> 提交(可运行任务):

它的返回值实际上是Future<Void>,子线程不会返回数据。

<T> Future<T> 提交(可运行任务,T 结果):

这个方法是不是很痛苦呢?返回的结果在调用的时候就已经给出了。如果我一开始就知道结果,为什么还要再次发起子线程呢?

事实上,这个结果可以作为一个代理。它不是实际结果,它只是存储结果。我这里举个例子方便大家理解:

final String[] result = new String[1];

Runnable r = new Runnable() {
    public void run() {
        result[0] = "hello world";
    }
};

Future<String[]> future = Executors.newSingleThreadExecutor().submit(r, result);
    
try {
    System.out.println("result[0]: " + future.get()[0]);
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

<T> Future<T> 提交(Callable<T> 任务):

这种方法比较容易理解。 Callable.call()方法在子线程中调用,并且它有一个返回值。直接返回加载的数据即可:

Future<String> future = Executors.newSingleThreadExecutor()
        .submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "Hello World";
            }
        });

try {
    System.out.print(future.get());
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

一个实际的例子

例如,当我们计算两个List<Integer>中的数字之和时,我们可以使用Future模式来提高效率:

public int getTotal(final List<Integer> a, final List<Integer> b) throws ExecutionException, InterruptedException {
    Future<Integer> future = Executors.newCachedThreadPool().submit(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            int r = 0;
            for (int num : a) {
                r += num;
            }
            return r;
        }
    });

    int r = 0;
    for (int num : b) {
        r += num;
    }
    return r + future.get();
}


转自:https://www.jianshu.com/p/949d44f3d9e3
 

. . .

相关推荐

额外说明

ToDesk版本更新,引入RTC传输技术,是否早以替代向日葵远程控制?

文章目录 前言 一、ToDesk 介绍 二、新版本体验介绍 1、RTC (Real Time Communication)技术 2、隐私屏保护用户信息 2.1、功能体验 2.2、功能限制 3、文件传输使用更加舒适 4、流畅度纵享丝滑 5、自定义键鼠映射

额外说明

浮点数的表示方法

把一个数的有效数字和数的范围在计算机的一个存储单元中分别予以表示。这种把数的范围和精度分别表示的方法,相当于数的小数点位置随比例因子的不同而在一定范围内可以自由浮动,所以称为浮点表示法。 在计算机中一个任意二进制数N可以写成: N=2^e.M 其中M称为

额外说明

javacv 简介、中文文档、中英对照文档 下载

javacv 文档 下载链接(含jar包、源码、pom) 组件名称 中英对照-文档-下载链接 中文-文档-下载链接 javacv-1.5.7.jar javacv-1.5.7-API文档-中英对照版.zip javacv-1.5.7-API文档-中文版.

额外说明

sqlalchemy一对多的关系表案例

本案例中直接使用sqlalchemy,没有使用flask-sqlalchemy来做的 1.引入包文件 # coding:utf-8 from flask import Flask, render_template,url_for,redirect imp

额外说明

安卓案例:利用帧动画实现游戏特效

文章目录 一、动画概念 二、逐帧动画 三、运行效果 四、涉及知识点 五、游戏特效案例实现思路 六、实现步骤 1、创建安卓应用【GameSpecialEffect】 2、将逐帧动画素材拷贝到drawable目录里 3、主布局资源文件acitivity_ma

额外说明

MYSQL 字符集问题

MySQL的字符集支持(Character Set Support)有两个方面:      字符集(Character set)和排序方式(Collation)。 对于字符集的支持细化到四个层次:      服务器(server),数据库(databas

额外说明

Windows系统丢失ReAgent.dll文件导致程序无法启动问题

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个ReAgent.dll文件(

额外说明

Win11系统提示由于找不到msvcr110.dll文件问题解决办法

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或者损坏了,这时你只需下载这个msvcr110.dll文件进行安装(前提是找到适合的版本),

额外说明

解决Windows丢失api-ms-win-core-file-l2-1-0.dll文件

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或者损坏了,这时你只需下载这个api-ms-win-core-file-l2-1-0.dll

额外说明

url wordpress_如何从WordPress网站URL摆脱/ wordpress /

网址 WordPress 您是否尝试从网站的 URL 中删除 /wordpress/?有时,初学者最终会在子目录中安装 WordPress,这会导致 /wordpress/ 出现在他们的网站 URL 中。 您想从网站的 URL 中删除 /wordpres

ads via 小工具