В чем разница между методом выполнения и методом отправки пула потоков?

Java JVM

Не завидуйте мандаринкам или бессмертным, а корректируйте строчку кода долго. Оригинал: Miss Sister Taste (идентификатор публичной учетной записи WeChat: xjjdog), добро пожаловать, пожалуйста, сохраните источник для перепечатки.

Содержание статьи очень сфокусировано, но полно галантереи. Вы можете попасть в ловушку, если не будете осторожны.

в параллельном пакетеExecutorService, это интерфейс, который наследуетExecutorExecutorЕсть только один метод.

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

Это метод execute, который принимает runnable и возвращает пустой. То есть после того, как он примет задачу, он будет работать молча и асинхронно.

Давайте еще раз посмотрим на метод submit. Разница в том, что метод submit возвращает объект Future. Очевидно, что это больше контента, чем метод execute.

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

проблема

Мы пытались запустить следующий код:

ExecutorService service = Executors.newFixedThreadPool(1);
Runnable r = () -> System.out.println(1 / 0);
service.submit(r);
service.shutdown();

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

Пучокsubmitметод заменен наexecuteметод, вы можете видеть, что исключение может быть выведено нормально. Чтобы избежать плагиата, позвольте мне экспортировать некоторые пользовательские стеки.

Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
	at com.github.xjjdog.pool.AAA.lambda$main$0(AAA.java:13)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

Но это также просто вывод, мы не можем использоватьlogbackВедение журнала, как запись, потому что она печатает это действие, которое мы не контролируемы.

Решение

Первый взглядsubmitпуть решения. Через возвращенное Future выполните его метод get, чтобы получить завершенный стек ошибок.

ExecutorService service = Executors.newFixedThreadPool(1);
Runnable r = () -> System.out.println(1 / 0);
Future f = service.submit(r);
f.get();
service.shutdown();

Ниже приведен вывод.

Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.github.xjjdog.pool.AAA.main(AAA.java:20)
Caused by: java.lang.ArithmeticException: / by zero
	at com.github.xjjdog.pool.AAA.lambda$main$0(AAA.java:16)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

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

Вы можете подозревать, что без вызова get наш код не запустится, но это не так. Измените runnable на следующий код, не вызывайте метод get и обнаружите, что программа выводит только одинa.

Runnable r = () -> {
    System.out.println("a");
    System.out.println(1 / 0);
};

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

Но, как мы упоминали выше, способ выполнения, ошибка не может быть поймана. На самом деле, мы можем пойти по кривой, чтобы спасти страну и решить ее. Решение заключается в использованииThreadFactory, которая реализует своюUncaughtExceptionHandler. Конкретный код выглядит следующим образом:

ThreadFactory factory = r->{
    Thread thread = Executors.defaultThreadFactory().newThread(r);
    thread.setUncaughtExceptionHandler( (t,e) -> {
        System.out.println(t + "" + e);
        e.printStackTrace();//example
    });
    return thread ;
};

ExecutorService service = Executors.newFixedThreadPool(1,factory);
Runnable r = () -> {
    System.out.println("a");
    System.out.println(1 / 0);
};

service.execute(r);
service.shutdown();

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

a
Thread[pool-1-thread-1,5,main]java.lang.ArithmeticException: / by zero

End

Я особенно выступаю против этих поведений по умолчанию бассейнов Java Thread для обработки исключений и различий. Можно сказать, что как поведение по умолчанию очень низкое, и нам все еще нужно иметь дело с большим количеством действий, чтобы поймать соответствующее исключение.

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

Об авторе:Мисс сестра вкус(xjjdog), публичная учетная запись, которая не позволяет программистам идти в обход. Сосредоточьтесь на инфраструктуре и Linux. Десять лет архитектуры, десятки миллиардов ежедневного трафика, обсуждение с вами мира высокой параллелизма, дающие вам другой вкус. Мой личный WeChat xjjdog0, добро пожаловать в друзья для дальнейшего общения.​