ExecutorService vs Casual Spawner

У меня есть основной вопрос о том, как ExecutorService работает на Java.

Довольно сложно увидеть разницу между простым Threads выполнять некоторые задачи параллельно и назначать каждую задачу ThreadPool,

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

Это просто вопрос, как выполнять свою работу быстрее, чем другой?

Вот два очень простых примера, чтобы показать разницу между этими двумя способами:

Использование службы исполнителя: Hello World (задача)

static class HelloTask implements Runnable {
    String msg;

    public HelloTask(String msg) {
        this.msg = msg; 
    }
    public void run() {
        long id = Thread.currentThread().getId();
        System.out.println(msg + " from thread:" + id);
    }
}

Использование сервиса executor: Hello World (создание исполнителя, отправка)

static class HelloTask {
    public static void main(String[] args) {
        int ntasks = 1000;
        ExecutorService exs = Executors.newFixedThreadPool(4);

        for (int i=0; i<ntasks; i++) { 
            HelloTask t = new HelloTask("Hello from task " + i);    
            exs.submit(t);
        }
        exs.shutdown();
    }
}

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

Использование службы исполнителя: Счетчик (задача)

static class HelloTaskRet implements Callable<Long> {
    String msg;

    public HelloTaskRet(String msg) {
        this.msg = msg; }

        public Long call() {
        long tid = Thread.currentThread().getId(); 
        System.out.println(msg + " from thread:" + tid); 
        return tid;
    } 
}

Использование службы исполнителя: (создание, отправка)

static class HelloTaskRet {
    public static void main(String[] args) {
        int ntasks = 1000;
        ExecutorService exs = Executors.newFixedThreadPool(4);

        Future<Long>[] futures = (Future<Long>[]) new Future[ntasks];

        for (int i=0; i<ntasks; i++) { 
            HelloTaskRet t = new HelloTaskRet("Hello from task " + i);
            futures[i] = exs.submit(t);
        }
        exs.shutdown();
    }
}

2 ответа

Решение

Хотя вопрос и пример кода не коррелируют, я постараюсь уточнить оба. Преимущество ExecutorService перед случайно порождаемыми потоками состоит в том, что он ведет себя предсказуемо и избегает накладных расходов на создание потоков, что является относительно большим для JVM (например, ему необходимо зарезервировать память для каждого потока). По предсказуемости, по крайней мере, для fixedThreadPoolЯ имею в виду, что вы знаете максимальное количество одновременных потоков и знаете, когда и как они могут быть созданы (чтобы ваша JVM не взорвалась в случае внезапных пиков).

Винс Эмиг: ExecutorService также поддерживает cachedThreadPool, который не имеет макс. Основная причина, по которой люди выбирают использовать ExecutorService заключается в предотвращении накладных расходов на создание нескольких потоков (с помощью рабочих потоков). В основном это используется в тех случаях, когда многие небольшие задачи должны выполняться в отдельном потоке. Также не забывайте о singleThreadExecutor,

Теперь по теме Runnable против CallableЭто легко увидеть из ваших примеров. Callables может вернуть значение заполнитель (Future) который в конечном итоге будет заполнен фактическим значением в будущем. Runnables не может ничего вернуть.

Винс Эмиг: Runnable также не может генерировать исключения, в то время как Callable Можно.

ExecutorService предоставляет много преимуществ по сравнению с простыми потоками

  1. Вы можете создавать / управлять / контролировать жизненный цикл потоков и оптимизировать накладные расходы на создание потоков
  2. Вы можете контролировать обработку заданий ( Work Stealing, ForkJoinPool, invokeAll) и т. Д.
  3. Вы можете планировать задачи в будущем времени
  4. Вы можете отслеживать прогресс и здоровье потоков

Даже для одной темы я предпочитаю использовать Executors.newFixedThreadPool(1);

Посмотрите на связанные вопросы SE:

Java Fork/Join vs ExecutorService - когда использовать какой?

Каковы преимущества использования ExecutorService?

Другие вопросы по тегам