ExecutorService против ThreadPoolExecutor с использованием LinkedBlockingQueue

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

Ниже приведен код с ExecutorService:

public class MultithreadingExample {

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executor = Executors.newFixedThreadPool(20);
        for (int i = 0; i < 100; i++) {
            executor.submit(new NewTask());
        }

        executor.shutdown();
        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
    }
}

class NewTask implements Runnable {

    @Override
    public void run() {
        //Measure the end to end latency of my client code
    }   
}

Описание проблемы:

Сейчас я читал какую-то статью в Интернете. Я обнаружил, что есть и ThreadPoolExecutor. Так что я запутался, какой из них мне следует использовать.

Если я заменю свой код выше:

ExecutorService executor = Executors.newFixedThreadPool(20);
    for (int i = 0; i < 100; i++) {
        executor.submit(new NewTask());
    }

to:

BlockingQueue<Runnable> threadPool = new LinkedBlockingQueue<Runnable>();

ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L, TimeUnit.MILLISECONDS, threadPool);

tpExecutor.prestartAllCoreThreads();

    for (int i = 0; i < 100; i++) {
        tpExecutor.execute(new NewTask());
    }

это будет иметь значение? Я пытаюсь понять, в чем разница между моим исходным кодом, использующим ExecutorService, и новым кодом, вставленным с использованием ThreadPoolExecutor. Некоторые из моих товарищей по команде сказали, что второй (ThreadPoolExecutor) - правильный способ использования.

Кто-нибудь может мне это прояснить?


person Community    schedule 23.04.2013    source источник


Ответы (6)


Вот источник Executors.newFixedThreadPool:

 public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

Он внутренне использует класс ThreadPoolExecutor с конфигурацией по умолчанию, как вы можете видеть выше. Теперь есть сценарии, в которых конфигурация по умолчанию не подходит, скажем, вместо LinkedBlockingQueue необходимо использовать приоритетную очередь и т. Д. В таких случаях вызывающий абонент может напрямую работать с базовым ThreadPoolExecutor, создав его экземпляр и передав ему желаемую конфигурацию.

person harsh    schedule 23.04.2013

тогда это будет иметь значение?

Это сделает ваш код более сложным без особой пользы.

Я пытаюсь понять, в чем разница между моим исходным кодом, который использует ExecutorService, и новым кодом, который я вставил и который использует ThreadPoolExectuor?

Почти ничего. Executors создает ThreadPoolExecutor для выполнения реальной работы.

Некоторые из моих товарищей по команде сказали, что второй (ThreadPoolExecutor) - правильный способ использования?

Просто потому, что это сложнее, не означает, что это правильно. Дизайнеры предоставили методы Executors.newXxxx, чтобы упростить вам жизнь и потому, что они ожидали, что вы будете использовать эти методы. Я предлагаю вам также использовать их.

person Peter Lawrey    schedule 23.04.2013
comment
Вы говорили о какой-то маленькой выгоде? Что это вообще такое? А за сценой Executors называли ThreadPoolExecutor, верно? Но я считаю, что он использует неограниченную очередь, которая, я думаю, немного медленная? Поправьте меня, если я ошибаюсь. - person ; 23.04.2013
comment
Небольшое преимущество в том, что вы можете играть с аргументами конструктора, но обычно это не очень хорошая идея. Вы предоставили такую ​​же неограниченную очередь, так что я не понимаю вашей точки зрения. Если он был медленным, зачем дизайнерам его использовать? - person Peter Lawrey; 23.04.2013
comment
Использование LinkedBoundedQueue требует определенных затрат. Это не имеет ничего общего с тем фактом, что он неограничен, и он очень мал по сравнению со стоимостью передачи задач между потоками, о чем, я полагаю, вы не слишком беспокоитесь. - person Peter Lawrey; 23.04.2013
comment
Для меня это важное различие, потому что мне нужно динамически регулировать количество потоков. - person Jonathan Rosenne; 10.08.2019

  1. Исполнители # newFixedThreadPool (int nThreads)

    ExecutorService executor = Executors.newFixedThreadPool(20);
    

в основном

 return new ThreadPoolExecutor(20, 20,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());

2.

BlockingQueue<Runnable> threadPool = new LinkedBlockingQueue<Runnable>();
ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L,
    TimeUnit.MILLISECONDS, threadPool);

Во втором случае вы просто увеличиваете maxPoolSize до 2000, что, я сомневаюсь, вам понадобится.

person AllTooSir    schedule 23.04.2013

Я считаю, что у RejectionHandler есть еще одно преимущество. Поправьте меня, если ошиблись

person Balaji Boggaram Ramanarayan    schedule 21.04.2014

В первом примере вы создали всего 20 потоков с помощью инструкции ниже

ExecutorService executor = Executors.newFixedThreadPool(20);

Во втором примере вы установили диапазон ограничений потока между 20 to 2000

 ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L, 
                                     TimeUnit.MILLISECONDS,threadPool);

Для обработки доступно больше потоков. Но вы настроили очередь задач как неограниченную.

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

ThreadPoolExecutor(int corePoolSize, 
               int maximumPoolSize, 
               long keepAliveTime, 
               TimeUnit unit, 
               BlockingQueue<Runnable> workQueue, 
               ThreadFactory threadFactory,
               RejectedExecutionHandler handler)

RejectedExecutionHandler был бы полезен, когда вы устанавливаете max capacity for workQueue и количество задач, которые были переданы Исполнителю, превышает workQueue емкость.

Взгляните на раздел "Отклоненные задачи" в ThreadPoolExecutor для более подробной информации.

person Ravindra babu    schedule 19.01.2016

Через 2 дня GC out of memory exception ThreadPoolExecutor спас мне жизнь. :)

Как сказал Баладжи:

[..] Еще одно преимущество - RejectionHandler.

В моем случае у меня было много RejectedExecutionException, и указание (как показано ниже) политики отмены решило все мои проблемы.

private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, cpus, 1, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());

Но будь осторожен! Он работает только в том случае, если вам не нужно выполнять все потоки, которые вы отправляете исполнителю.

Дополнительную информацию о ThreadPoolExecutor см. В ответе Даррена

person Community    schedule 10.11.2015