Подробное объяснение класса JAVA Future

интервью Java
Подробное объяснение класса JAVA Future

Есть чувства, есть галантерейные товары, поиск в WeChat【Третий принц Ао Бин] Подпишитесь на этого программиста, у которого есть кое-что.

эта статьяGitHub github.com/JavaFamilyВключено, и есть полные тестовые площадки, материалы и мой цикл статей для интервью с производителями первой линии.

предисловие

Параллельное программирование стало чрезвычайно важной частью высокопроизводительного программирования. Когда производительность одноядерного ЦП достигает своего предела, мы можем только еще больше повысить производительность системы за счет многоядерности, тем самым породив параллельное программирование.

Поскольку параллельное программирование сложнее и подвержено ошибкам, чем последовательное программирование, нам нужно учиться на некоторых превосходных и зрелых шаблонах проектирования предшественников, чтобы сделать наши проекты более надежными и совершенными.

Шаблон Future — один из наиболее широко используемых и чрезвычайно важных шаблонов проектирования. Узнайте о режиме будущего с помощью A Bing уже сегодня!

Будущий режим в жизни

Чтобы быстрее понять режим Future, давайте сначала рассмотрим пример из реальной жизни.

Сцена 1:

Время обеда, студенты собирались поесть, Ванга спустилась по лестнице, прогулялась 20 минут, пришла в KFC, сделала заказ, встала в очередь, поела, потратила в общей сложности 20 минут, и еще 20 минут вернулась в компанию, чтобы продолжить работу. , всего 1 час.

сцена 2

Когда подошло время обеда, одноклассники собирались есть, Сяо Ван заказал еду на вынос в KFC, и вскоре получил заказ (хотя заказ нельзя есть как еду, но с заказом, я боюсь, что я не сможет есть). Затем Сяо Ван смог продолжить работу.Через 30 минут принесли еду на вынос.Затем Сяо Ван провел 10 минут за едой,а затем снова смог продолжить работу.Он успешно занимался с Сяо Ван по соседству.

Очевидно, что в этих двух сценариях рабочие часы Сяо Вана более компактны, особенно те часы, которые стоят в очередях, могут быть оставлены сотрудникам службы доставки, чтобы он мог больше сосредоточиться на своей работе. Смарт, вы должны были понять, что сценарий 1 — это типичный синхронный вызов функции, а сценарий 2 — типичный асинхронный вызов.

Еще одна особенность асинхронного вызова в сценарии 2 заключается в том, что он имеет возвращаемое значение, которое представляет собой наш заказ. Этот порядок очень важен, при таком порядке мы можем получить результат, соответствующий текущему вызову.

Порядок здесь подобен Будущему в режиме Будущего, который является контрактом и обещанием. Хотя заказ нельзя есть, но держа заказ в руках, он не боится не есть, хотя Будущее - это не тот результат, который мы хотим, но удерживая Будущее, мы можем получить желаемый результат в будущем.

Таким образом, шаблон Future является хорошим решением для тех асинхронных вызовов, которые должны возвращать значение.

Основные роли в будущем рисунке

Типичный паттерн Future состоит из следующих частей:

  • Main: Система запускается, и Клиент вызывается, чтобы сделать запрос
  • Клиент: вернуть объект Data, немедленно вернуть FutureData и запустить поток ClientThread для сборки RealData.
  • Данные: интерфейс, который возвращает данные
  • FutureData: Будущие данные, построение происходит очень быстро, но это виртуальные данные, которые необходимо собрать с помощью RealData, как заказ.
  • ReadaTA: Реальные данные, его структура медленная, как KFC обед в приведенном выше примере.

Отношения между ними показаны ниже:

Среди них стоит отметить Data, RealData и FutureData. Это типичный набор режимов прокси.Интерфейс Data представляет внешние данные, а RealData представляет реальные данные.Это как обед, который стоит дорого и занимает много времени.Относительно FutureData, как прокси RealData, аналогично заказу/контракту. , через FutureData RealData можно получить в будущем.

Таким образом, паттерн Future по сути является практическим применением паттерна Proxy.

Реализуйте простой шаблон Future

Основываясь на приведенном выше дизайне, давайте реализуем простой шаблон прокси!

Первый — это интерфейс данных, который представляет данные:

public interface Data {
    public String getResult ();
}

Затем есть FutureData, который также является ядром всего шаблона Future:

public class FutureData implements Data {
    // 内部需要维护RealData
    protected RealData realdata = null;          
    protected boolean isReady = false;
    public synchronized void setRealData(RealData realdata) {
        if (isReady) { 
            return;
        }
        this.realdata = realdata;
        isReady = true;
        //RealData已经被注入,通知getResult()
        notifyAll();                            			
    }
    //会等待RealData构造完成
    public synchronized String getResult() {        	
        while (!isReady) {
            try {
                //一直等待,直到RealData被注入
                wait();                        			
            } catch (InterruptedException e) {
            }
        }
        //真正需要的数据从RealData获取
        return realdata.result;                    		
    }
}

Вот по-настоящему:

public class RealData implements Data {
    protected final String result;
    public RealData(String para) {
        StringBuffer sb=new StringBuffer();
        //假设这里很慢很慢,构造RealData不是一个容易的事
        result =sb.toString();
    }
    public String getResult() {
        return result;
    }
}

Затем получите данные от клиента:

public class Client {
    //这是一个异步方法,返回的Data接口是一个Future
    public Data request(final String queryStr) {
        final FutureData future = new FutureData();
        new Thread() {                                      
            public void run() {                    	
                // RealData的构建很慢,所以在单独的线程中进行
                RealData realdata = new RealData(queryStr);
                //setRealData()的时候会notify()等待在这个future上的对象
                future.setRealData(realdata);
            }                                               
        }.start();
        // FutureData会被立即返回,不会等待RealData被构造完
        return future;                        		
    }
}

Последняя функция Main, объединяющая все вместе:

public static void main(String[] args) {
    Client client = new Client();
    //这里会立即返回,因为得到的是FutureData而不是RealData
    Data data = client.request("name");
    System.out.println("请求完毕");
    try {
        //这里可以用一个sleep代替了对其他业务逻辑的处理
        //在处理这些业务逻辑的过程中,RealData被创建,从而充分利用了等待时间
        Thread.sleep(2000);
    } catch (InterruptedException e) {
    }
    //使用真实的数据,如果到这里数据还没有准备好,getResult()会等待数据准备完,再返回
    System.out.println("数据 = " + data.getResult());
}

Это простейшая реализация паттерна Future, хотя она и проста, но уже содержит наиболее существенную часть паттерна Future. Всем очень важно понимать объект Future внутри JDK.

Шаблон будущего в Java

Режим Future настолько широко используется, что имеет относительно полную реализацию и поддержку в JDK. Далее давайте взглянем на реализацию Future внутри JDK:

Прежде всего, внутри JDK есть интерфейс Future, который аналогичен указанному выше порядку.Конечно, как полноценный коммерческий продукт, функции Future здесь больше.В дополнение к методу get() для получения реального data, он также предоставляет набор вспомогательных методов, таких как:

  • Cancel(): Если вы ждете слишком долго, вы можете отменить задачу напрямую.
  • isCancelled(): задача была отменена?
  • isDone(): завершена ли задача
  • get(): существует 2 метода get(), один из которых без параметров означает бесконечное ожидание, или вы можете просто ждать заданное время

Следующий код демонстрирует использование этого Future:

        //异步操作 可以用一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(1);
        //执行FutureTask,相当于上例中的 client.request("name") 发送请求
        //在这里开启线程进行RealData的call()执行
        Future<String> future = executor.submit(new RealData("name"));
        System.out.println("请求完毕,数据准备中");
        try {
            //这里依然可以做额外的数据操作,这里使用sleep代替其他业务逻辑的处理
            Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
        //如果此时call()方法没有执行完成,则依然会等待
        System.out.println("数据 = " + future.get());

Весь процесс использования очень прост, давайте проанализируем, что произошло в executor.submit():

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        // 根据Callable对象,创建一个RunnableFuture,这里其实就是FutureTask
        RunnableFuture<T> ftask = newTaskFor(task);
        //将ftask推送到线程池
        //在新线程中执行的,就是run()方法,在下面的代码中有给出
        execute(ftask);
        //返回这个Future,将来通过这个Future就可以得到执行的结果
        return ftask;
    }
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

Самая важная часть приведена ниже. Когда FutureTask выполняется как поток, он сохраняет результат в исходе и устанавливает статус задачи. Ниже приведен метод run() FutureTask:

Результаты, полученные из FutureTask, реализуются следующим образом:

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        //如果没有完成,就等待,回到用park()方法阻塞线程
        //同时,所有等待线程会在FutureTask的waiters字段中排队等待
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    private V report(int s) throws ExecutionException {
        //outcome里保存的就是最终的计算结果
        Object x = outcome;
        if (s == NORMAL)
            //正常完成,就返回outcome
            return (V)x;
        //如果没有正常完成, 比如被用户取消了,或者有异常了,就抛出异常
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

Высокоуровневая версия шаблона Future — CompletableFuture

Хотя режим Future прост в использовании, есть и проблема, то есть после того, как задача передана в поток, вызывающий поток не знает, когда задача будет завершена.Необходимое ожидание, тогда пропускная способность системы затруднена. улучшить.

Чтобы решить эту проблему, JDK усилил режим Future и создал CompletableFuture, который можно понимать как обновленную версию режима Future.Его самая большая функция — предоставить механизм обратного вызова, который может автоматически вызывать некоторые последующие действия после задача завершена.Так вся программа может полностью убрать "ожидание результата".

Вот простой пример:

В этом примере впервые создается асинхронный вызов на основе GetPrice (), и затем последующая операция установлена ​​с использованием метода The ToCcept (), то есть последующая обработка после GetPrice () выполняется.

Нетрудно заметить, что CompletableFuture более практичен, чем обычный Future, потому что он может автоматически вызывать следующую операцию после успешного выполнения Future, так что у всей программы не будет блокирующего места (то есть вы не нужно везде ждать.Выполнение Future, но пусть Future успешно выполнится, оно вам автоматически подскажет).

Взяв приведенный выше код в качестве примера, все волшебные функции CompletableFuture полностью благодаря классу AsyncSupply (созданному методом SupplyAsync() в приведенном выше коде).

Когда AsyncSupply выполняется, это выглядит так:

        public void run() {
            CompletableFuture<T> d; Supplier<T> f;
            if ((d = dep) != null && (f = fn) != null) {
                dep = null; fn = null;
                if (d.result == null) {
                    try {
                        //这里就是你要执行的异步方法
                        //结果会被保存下来,放到d.result字段中
                        d.completeValue(f.get());
                    } catch (Throwable ex) {
                        d.completeThrowable(ex);
                    }
                }
                //执行成功了,进行后续处理,在这个后续处理中,就会调用thenAccept()中的消费者
                //这里就相当于Future完成后的通知
                d.postComplete();
            }
        }

Продолжайте смотреть на d.postComplete(), где будет вызываться серия последующих операций

   final void postComplete() {
                //省略部分代码,重点在tryFire()里
                //在tryFire()里,真正触发了后续的调用,也就是thenAccept()中的部分
                f = (d = h.tryFire(NESTED)) == null ? this : d;
            }
        }
    }

болтовня

Сегодня мы в основном вводим режим Future.Мы начинаем с самого простого режима Future и постепенно его углубляем.Последовательно вводим реализацию режима Future внутри JDK,и кратко знакомим с эволюционной версией режима Future,ComletableFuture. правильно

С точки зрения многопоточной разработки чрезвычайно широко используется режим Future, и можно сказать, что этот режим стал инфраструктурой для асинхронной разработки.

Что ж, если вы хотите узнать больше о многопоточности, вы можете подписаться на меня, проверить исторические статьи, и я буду продолжать их обновлять.

Я Ао Бин,Чем больше вы знаете, тем больше вы не знаете, спасибо за ваши таланты:как,собиратьиКомментарий, увидимся в следующий раз!


Статья постоянно обновляется, вы можете искать в WeChat "Третий принц Ао Бин"Прочтите это в первый раз, ответьте [материал] Подготовленные мной материалы интервью и шаблоны резюме крупных заводов первой линии, эта статьяGitHub github.com/JavaFamilyОн был включен, и есть полные тестовые сайты для интервью с крупными заводами.Добро пожаловать в Star.