2010-02-24 21 views
49

Siempre he leído que crear hilos es costoso.
También sé que no puede volver a ejecutar un hilo.reutilización de hilos java

que ver en el documento de Executors clase:

Crea un grupo de subprocesos que crea nuevos temas, según sea necesario, pero volverá a utilizar hilos ya construidos cuando están disponibles.

Mind the word 'reuse'.

¿Cómo roscan los hilos 'reutilizar'?

Respuesta

44

creo que entendí lo que se confuzzabling así que aquí está mi respuesta más larga: la terminología es un poco pequeña engañosa (obviamente, o no lo haría esa pregunta poniendo específicamente el énfasis en la 'reutilización'):

¿Cómo roscan los hilos 'reutilizar'?

Lo que ocurre es que un solo hilo se puede utilizar para procesar varias tareas (normalmente pasan como Runnable, pero esto depende de su marco 'ejecutor': los ejecutores defecto acepta Runnable, pero se puede escribir su propia "ejecutor "/ thread-pool acepta algo más complejo que Runnable [como, digamos, CancellableRunnable]).

Ahora en la implementación predeterminada ExecutorService si un hilo se termina de alguna manera mientras todavía está en uso, se reemplaza automáticamente con un nuevo hilo, pero esto no es la 'reutilización' de la que están hablando. No hay "reutilización" en este caso.

Por lo tanto, es cierto que no se puede llamar start() en un hilo de Java doble pero se puede pasar la mayor cantidad Runnable como usted quiere un ejecutor y cada run() método Runnable 's se reunirá una vez.

Puede pasar 30 Runnable a 5 de Java Thread y cada subproceso de trabajo puede estar llamando, por ejemplo, run() 6 veces (prácticamente no hay garantía de que usted va a encontrarse cumpliendo exactamente 6 Runnable por Thread pero eso es un detalle).

En este ejemplo, start() se habría llamado 6 veces. Cada uno estos 6 start() llamarán exactamente una vez el método run() de cada Thread:

De Thread.start() Javadoc:

* Causes this thread to begin execution; the Java Virtual Machine 
* calls the <code>run</code> method of this thread. 

PERO luego dentro método de cada hilo run() se quita de la cola Runnable y se llamará al método run() de cada Runnable. De modo que cada subproceso puede procesar varios Runnable. A eso se refieren por "reutilización de hilo".

Una manera de hacer su propio grupo de subprocesos es utilizar una cola de bloqueo en la cual se ponga en cola runnables y tienen cada uno de sus hilos, una vez que termine de procesarse el método de un Runnablerun(), dequeue la siguiente Runnable (o bloque) y ejecute su método run(), luego enjuague y repita.

supongo que parte de la confusión (y es un poco confuso) viene del hecho de que un Thread toma un Runnable y al llamar start()run() método de la Ejecutable 's se llama mientras que los grupos de subprocesos predeterminado también toman Runnable .

+22

tl; dr Los subprocesos del grupo de subprocesos básicamente se ejecutan bucles que extraen las tareas enviadas de una cola.Los subprocesos no dejan de ejecutarse cuando sirven una tarea, solo esperan a que el siguiente se envíe a la cola. Nunca se "vuelven a ejecutar" como se le preguntó en la pregunta, ya que solo están funcionando constantemente. – Sogger

4

El grupo de subprocesos consta de varios subprocesos de trabajador fijo que pueden tomar tareas de una cola de tareas interna. Por lo tanto, si una tarea finaliza, el hilo no termina en, pero espera la siguiente tarea. Si abortas un hilo, este se reemplaza automáticamente.

Mira el documentation para más detalles.

-4

Si el thread está terminado, seguro que puede volver a utilizarlo. Puede asegurarse de que no se está ejecutando llamando al isAlive() o algo similar.

EDIT: Si alguien ha suspendido un thread, uno no puede comenzar de nuevo. No entiendo por qué no se puede iniciar si ha terminado normalmente. Pero doy el beneficio de la duda y digo que no puede ser reutilizado.

+4

@fastcodejava: Si termina un hilo, no puede volver a llamar * start() *, eso le daría una * IllegalThreadStateException *. – SyntaxT3rr0r

12

El método de subprocesos run en un grupo de subprocesos no consiste únicamente en ejecutar una única tarea. El método run de un subproceso en un grupo de subprocesos contiene un bucle. Retira una tarea de una cola, ejecuta la tarea (que devuelve de vuelta al ciclo cuando se completa), y luego obtiene la siguiente tarea. El método run no se completa hasta que el hilo ya no se necesita.

Editado para añadir:

Aquí es el método run de la clase interna Worker en ThreadPoolExecutor.

696:   /** 
697:   * Main run loop 
698:   */ 
699:   public void run() { 
700:    try { 
701:     Runnable task = firstTask; 
702:     firstTask = null; 
703:     while (task != null || (task = getTask()) != null) { 
704:      runTask(task); 
705:      task = null; // unnecessary but can help GC 
706:     } 
707:    } finally { 
708:     workerDone(this); 
709:    } 
710:   } 
+0

@Mike Daniels: exactamente ... Dicho eso, debes amar ese código Sun que asigna * "tarea = nulo;" * y luego comenta * "// innecesaria pero puede ayudar a GC" *. Deberías publicar un código aquí al hacer eso y te darían un salto en la garganta por parte de groupthinkers que te dirían lo inútil que es;) – SyntaxT3rr0r

+5

@Wizard: ¡En realidad no es innecesario - la (extraña) prueba para la tarea! = nulo en el ciclo hace que sea necesario evitar el procesamiento continuo de la misma tarea. Incluso si el bucle fuera más convencional, la tarea de anulación sería buena porque, de lo contrario, si getTask() bloquea durante un tiempo prolongado, el GC de la tarea se retrasaría por el mismo período de tiempo. –

+0

@Software Monkey: muy perspicaz sobre el segundo punto. 'getTask' probablemente salga de una cola de bloqueo con un tiempo de espera excedido. Eso podría bloquear durante todo el tiempo de espera. Si la tarea tiene muchos campos (para los parámetros pasados ​​a la tarea), el GC anterior sería útil. –

0

Un grupo de subprocesos crea sus propios subprocesos y proporciona sus propios Runnables poco inteligentes para esos subprocesos. Esos Runnables nunca terminan sino que se sincronizan en una cola (esperan()) hasta que un Callable esté presente en esa cola; se les notifica cuando eso sucede y Runnable ejecuta el invocable desde la cola y todo el escenario se repite nuevamente.