java - pools - ExecutorService vs ThreadPoolExecutor mediante LinkedBlockingQueue
java executors (6)
¿Entonces eso hará alguna diferencia?
Hará su código más complicado por poco beneficio.
¿Estoy tratando de entender cuál es la diferencia entre mi código original que está usando ExecutorService y el nuevo código que pegué que está usando ThreadPoolExectuor?
Casi nada. Executors
crean un ThreadPoolExecutor para hacer el trabajo real.
¿Algunos de mis compañeros de equipo dijeron que el segundo (ThreadPoolExecutor) es la forma correcta de usar?
El hecho de que sea más complicado no significa que sea lo correcto. Los diseñadores proporcionaron los métodos Executors.newXxxx para simplificarle la vida y porque esperaban que usara esos métodos. Te sugiero que los uses también.
Estoy trabajando en un proyecto multiproceso en el que necesito generar múltiples subprocesos para medir el rendimiento de principio a fin de mi código de cliente, mientras hago pruebas de carga y rendimiento. Así que creé el siguiente código que está usando ExecutorService
.
A continuación se muestra el código con 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
}
}
Planteamiento del problema:
Ahora estaba leyendo un artículo en internet. Descubrí que también hay ThreadPoolExecutor
. Así que me confundí cuál debería estar usando.
Si sustituyo mi código anterior de:
ExecutorService executor = Executors.newFixedThreadPool(20);
for (int i = 0; i < 100; i++) {
executor.submit(new NewTask());
}
a:
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());
}
¿Esto hará alguna diferencia? Estoy tratando de entender cuál es la diferencia entre mi código original usando ExecutorService
y el nuevo código pegado usando ThreadPoolExecutor
. Algunos de mis compañeros de equipo dijeron que el segundo (ThreadPoolExecutor) es la forma correcta de usar.
¿Alguien puede aclarar esto para mí?
Ejecutores # newFixedThreadPool (int nThreads)
ExecutorService executor = Executors.newFixedThreadPool(20);
es básicamente
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);
En el segundo caso, solo está aumentando maxPoolSize a 2000, lo cual dudo que necesite.
Aquí está la fuente de Executors.newFixedThreadPool
:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Utiliza ThreadPoolExecutor
clase ThreadPoolExecutor
con la configuración predeterminada como puede ver arriba. Ahora hay escenarios en los que la configuración predeterminada no es adecuada, por ejemplo, en lugar de LinkedBlockingQueue
debe LinkedBlockingQueue
una cola de prioridad, etc. En tales casos, la persona que llama puede trabajar directamente en ThreadPoolExecutor
subyacente al crear una instancia y pasarle la configuración deseada.
Creo que una ventaja más es con RejectionHandler. Corrigeme si mal
Después de 2 días de GC out of memory exception
de GC out of memory exception
, ThreadPoolExecutor
me salvó la vida. :)
Como Balaji dijo:
[..] Una ventaja más es con RejectionHandler.
En mi caso, tuve un montón de RejectedExecutionException
y especificando (como sigue) la política de descarte resolvió todos mis problemas.
private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, cpus, 1, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());
¡Pero ten cuidado! Solo funciona si no necesita ejecutar todos los subprocesos que envía al ejecutor.
Para obtener más información sobre ThreadPoolExecutor
eche un vistazo a la respuesta de Darren
En el primer ejemplo, ha creado solo 20 hilos con la siguiente declaración
ExecutorService executor = Executors.newFixedThreadPool(20);
En el segundo ejemplo, ha establecido el rango de límites de hilo entre 20 to 2000
ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L,
TimeUnit.MILLISECONDS,threadPool);
Más hilos están disponibles para su procesamiento. Pero ha configurado la cola de tareas como una cola ilimitada.
ThreadPoolExecutor sería más efectivo si ha personalizado muchos o todos los parámetros a continuación.
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
RejectedExecutionHandler
sería útil cuando establece la max capacity for workQueue
y la cantidad de tareas que se han enviado a Executor son más que la capacidad de workQueue
.
Eche un vistazo a la sección de tareas rechazadas en ThreadPoolExecutor para obtener más detalles.