5 асинхронных вызовов синхронных методов Java, которые необходимо знать

Java задняя часть сервер
Пожалуйста, указывайте адрес этой статьи при перепечатке:woo woo Краткое описание.com/fear/ отправить 00 ах ах 6 отправить 66…
Адрес источника:git ee.com/sunny more/ ах…

Санни сначала расскажет о понимании асинхронности и синхронности:

Синхронный вызов: вызывающий объект продолжает ожидать возвращаемого результата в процессе вызова. Асинхронный вызов: во время вызывающего процесса вызывающий объект не ожидает непосредственно возвращаемого результата, а выполняет другие задачи, а возвращаемый результат обычно имеет форму функции обратного вызова.

На самом деле, разница между ними все еще очень очевидна, и я не буду здесь вдаваться в подробности, а поговорим в основном о том, как Java преобразует асинхронные вызовы в синхронизацию. Другими словами, необходимо непрерывно блокировать до тех пор, пока результат вызова не будет получен в процессе асинхронного вызова. Чтобы не продаваться, сначала перечислите пять методов, а потом по порядку приведите примеры:

  1. Использование методов ожидания и уведомления
  2. Используйте условные блокировки
  3. Future
  4. Использование защелки обратного отсчета
  5. Использование циклического барьера

0. Создайте асинхронный вызов

Прежде всего, чтобы написать демонстрацию, вам нужно сначала написать инфраструктуру Здесь вам нужно построить модель асинхронного вызова. Класс асинхронного вызова:

public class AsyncCall {

    private Random random = new Random(System.currentTimeMillis());

    private ExecutorService tp = Executors.newSingleThreadExecutor();

    //demo1,2,4,5调用方法
    public void call(BaseDemo demo){

        new Thread(()->{
            long res = random.nextInt(10);

            try {
                Thread.sleep(res*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            demo.callback(res);
        }).start();


    }

    //demo3调用方法
    public Future<Long> futureCall(){

        return tp.submit(()-> {
            long res = random.nextInt(10);

            try {
                Thread.sleep(res*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return res;
        });

    }

    public void shutdown(){

        tp.shutdown();

    }

}

В основном нас интересует метод call.Этот метод получает демонстрационный параметр, запускает поток, выполняет определенные задачи в потоке и использует метод обратного вызова демо для вызова функции обратного вызова. Вы заметили, что возвращаемый результат здесь представляет собой длинное целое число [0,10), а результат — несколько, пусть поток спит, как долго — это в основном для лучшего наблюдения за экспериментальными результатами и имитации времени обработки в процесс асинхронного вызова.
Что касается методов futureCall и shutdown, а также пула потоков tp, все они подготовлены для реализации в demo3 с использованием Future.
Базовый класс для демонстрации:

public abstract class BaseDemo {

    protected AsyncCall asyncCall = new AsyncCall();

    public abstract void callback(long response);

    public void call(){
        System.out.println("发起调用");
        asyncCall.call(this);
        System.out.println("调用返回");
    }

}

BaseDemo очень простой.Он содержит экземпляр класса асинхронного вызова, и есть метод call для инициирования асинхронных вызовов.Конечно, есть еще абстрактный метод callback, который должен быть реализован каждым демо-в основном в обратном вызове для выполнения соответствующей обработки для достижения асинхронного вызова синхронной цели.

1. Используйте методы ожидания и уведомления

Этот метод фактически использует механизм блокировки и напрямую вставляет код:

public class Demo1 extends BaseDemo{

    private final Object lock = new Object();

    @Override
    public void callback(long response) {
        System.out.println("得到结果");
        System.out.println(response);
        System.out.println("调用结束");

        synchronized (lock) {
            lock.notifyAll();
        }

    }

    public static void main(String[] args) {

        Demo1 demo1 = new Demo1();

        demo1.call();

        synchronized (demo1.lock){
            try {
                demo1.lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("主线程内容");

    }
}

Видно, что после того, как вызов инициирован, основной поток использует ожидание для блокировки, ожидая вызова метода notify или notifyAll в обратном вызове для пробуждения. Обратите внимание, что, как всем известно, и ждать, и уведомлять здесь нужно сначала получить блокировку объекта. В основном потоке мы наконец печатаем контент, который также используется для проверки результатов эксперимента.Если нет ожидания и уведомления, содержимое основного потока будет напечатано сразу после содержимого вызова, и как наш код выше, содержимое основного потока всегда будет ждать. Оно не будет печататься до тех пор, пока не завершится вызов функции обратного вызова. Без использования синхронной операции выведите результат:

发起调用
调用返回
主线程内容
得到结果
1
调用结束

И после использования синхронной операции:

发起调用
调用返回
得到结果
9
调用结束
主线程内容

2. Используйте условные блокировки

Аналогично принципу метода 1:

public class Demo2 extends BaseDemo {

    private final Lock lock = new ReentrantLock();
    private final Condition con = lock.newCondition();

    @Override
    public void callback(long response) {

        System.out.println("得到结果");
        System.out.println(response);
        System.out.println("调用结束");
        lock.lock();
        try {
            con.signal();
        }finally {
            lock.unlock();
        }

    }

    public static void main(String[] args) {

        Demo2 demo2 = new Demo2();

        demo2.call();

        demo2.lock.lock();

        try {
            demo2.con.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            demo2.lock.unlock();
        }
        System.out.println("主线程内容");
    }
}

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

3. Future

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

public class Demo3{

    private AsyncCall asyncCall = new AsyncCall();

    public Future<Long> call(){

        Future<Long> future = asyncCall.futureCall();

        asyncCall.shutdown();

        return future;

    }

    public static void main(String[] args) {

        Demo3 demo3 = new Demo3();

        System.out.println("发起调用");
        Future<Long> future = demo3.call();
        System.out.println("返回结果");

        while (!future.isDone() && !future.isCancelled());

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

        System.out.println("主线程内容");

    }
}

Мы вызываем метод futureCall. Метод отправит Callable в пул потоков tp, а затем вернет Future. Это Future получено из вызова в нашей демонстрации 3. После получения объекта future мы можем закрыть пул потоков и вызвать метод отключения asyncCall. Есть одно замечание по поводу закрытия пула потоков, давайте вернемся и посмотрим на метод выключения asyncCall:

public void shutdown(){

        tp.shutdown();

    }

Обнаружено, что просто вызывается метод отключения пула потоков, и тогда мы говорим, внимание Лучше всего не использовать здесь метод tp shutdownNow, который попытается прервать выполнение задачи в потоке; то есть, если вы используете этот метод, возможно, что задача, соответствующая нашему будущему, будет прервана, и результат выполнения не может быть получен. Затем мы обращаем внимание на содержимое в основном потоке, блокировка основного потока реализуется нами, а состояние выполнения оценивается по isDone и isCancelled будущего, пока выполнение не будет завершено или отменено. Затем мы печатаем результат get.

4. Использование CountDownLatch

Использование CountDownLatch, вероятно, является наиболее распространенным в повседневном программировании, и оно также выглядит относительно элегантно:

public class Demo4 extends BaseDemo{

    private final CountDownLatch countDownLatch = new CountDownLatch(1);

    @Override
    public void callback(long response) {

        System.out.println("得到结果");
        System.out.println(response);
        System.out.println("调用结束");

        countDownLatch.countDown();

    }

    public static void main(String[] args) {

        Demo4 demo4 = new Demo4();

        demo4.call();

        try {
            demo4.countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程内容");

    }
}

Как вы обычно используете, здесь метод await CountDownLatch используется для блокировки в основном потоке, а метод countDown используется в обратном вызове, чтобы заставить ожидающую часть других потоков продолжать работать. Конечно, это то же самое, что и в demo1 и demo2.Для заблокированной части основного потока можно установить таймаут, и блок уже не может быть заблокирован по истечении таймаута.

5. Использование циклического барьера

Ситуация CyclicBarrier чем-то похожа на CountDownLatch:

public class Demo5 extends BaseDemo{

    private CyclicBarrier cyclicBarrier = new CyclicBarrier(2);


    @Override
    public void callback(long response) {

        System.out.println("得到结果");
        System.out.println(response);
        System.out.println("调用结束");

        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {

        Demo5 demo5 = new Demo5();

        demo5.call();

        try {
            demo5.cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }

        System.out.println("主线程内容");

    }
}

Обратите внимание, что CyclicBarrier и CountDownLatch только похожи, и между ними есть определенные различия. Например, одно можно понимать как сложение, и оно будет выполняться вместе после прибавления к этому числу; другое — это вычитание, и оно будет продолжать выполняться при уменьшении до 0. Один повторяемый, другой не бла-бла-бла. Кроме того, при использовании CyclicBarrier следует обратить внимание на два момента. Во-первых, при инициализации номер параметра должен быть установлен в 2, потому что асинхронный вызов здесь является потоком, а основной поток — потоком, и выполнение может продолжаться, когда оба потока ждут, что также является частью, которая отличается от CountDownLatch. Второй момент так же касается значения параметра инициализации, который здесь к демо отношения не имеет.При программировании нужно быть более внимательным.Если это значение задано слишком большим, то оно больше, чем количество потоков в пул потоков, то это очень легко вызвало тупик.

Суммировать

Подводя итог, на этот раз необходимо сказать о нескольких методах. По сути все методы основаны на одном принципе, то есть блокировка и ожидание результата в вызывающем потоке, и разблокировка состояния блокировки в callback-функции. Если у вас есть другие методы, добро пожаловать на обсуждение со мной~

Почта:zsunny@yeah.net

Эта статья приветствуется для перепечатки, пожалуйста, укажите адрес этой статьи:woo woo Краткое описание.com/fear/ отправить 00 ах ах 6 отправить 66…