Desde el tiempo que pasé con hilos en Java, he encontrado estas dos formas de escribir hilos:
Con implements Runnable
:
public class MyRunnable implements Runnable {
public void run() {
//Code
}
}
//Started with a "new Thread(new MyRunnable()).start()" call
O con extends Thread
:
public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
//Code
}
}
//Started with a "new MyThread().start()" call
¿Hay alguna diferencia significativa en estos dos bloques de código?
java
multithreading
runnable
implements
java-threads
usuario65374
fuente
fuente
interrupt()
. Nuevamente, es una idea, podría ser útil en el caso correcto, sin embargo, no lo recomiendo.Respuestas:
Sí: implementos
Runnable
es la forma preferida de hacerlo, en mi opinión. Realmente no estás especializando el comportamiento del hilo. Solo le estás dando algo para correr. Eso significa que la composición es el camino filosóficamente "más puro".En términos prácticos , significa que también puede implementar
Runnable
y ampliar desde otra clase.fuente
if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
extends Thread
y, si no desea enhebrar, ¿por qué implementaríaRunnable
...tl; dr: implementa Runnable es mejor. Sin embargo, la advertencia es importante
En general, recomendaría usar algo así como en
Runnable
lugar deThread
porque le permite mantener su trabajo solo libremente con su elección de concurrencia. Por ejemplo, si usa unRunnable
y decide más adelante que esto no requiere de hechoThread
, puede llamar a threadA.run ().Advertencia: Por aquí, desaconsejo el uso de hilos sin procesar. Prefiero el uso de Callables y FutureTasks (del javadoc: "Un cálculo asincrónico cancelable"). La integración de los tiempos de espera, la cancelación adecuada y la agrupación de subprocesos del soporte de concurrencia moderna son mucho más útiles para mí que las pilas de subprocesos sin procesar.
Seguimiento: hay un
FutureTask
constructor que le permite usar Runnables (si eso es con lo que se siente más cómodo) y aún así obtener el beneficio de las modernas herramientas de concurrencia. Para citar el javadoc :Si no necesita un resultado en particular, considere usar construcciones de la forma:
Entonces, si reemplazamos su
runnable
con suthreadA
, obtenemos lo siguiente:Otra opción que le permite estar más cerca de Runnables es un ThreadPoolExecutor . Puede usar el método de ejecución para pasar un Runnable para ejecutar "la tarea dada en algún momento en el futuro".
Si desea intentar usar un grupo de subprocesos, el fragmento de código anterior se convertiría en algo similar a lo siguiente (usando el método de fábrica Executors.newCachedThreadPool () ):
fuente
es
sería mejor como un campo estático (o inyectado), por lo que solo se crea una vez.FutureTask
directamente no es lo que quieres hacer.ExecutorService
s va a crear la apropiadaFuture
para cuandosubmit
unRunnable
/Callable
a ellos. Lo mismo sucede conScheduledExecutorService
s, yScheduledFuture
cuandoschedule
unRunnable
/Callable
.Moraleja de la historia:
Herede solo si desea anular algún comportamiento.
O más bien debería leerse como:
Herede menos, interactúe más.
fuente
run()
método.java.lang.Thread
anulando elrun()
método. En ese caso, debes anular elstart()
método, supongo. Normalmente, simplemente reutiliza el comportamiento de lajava.lang.Thread
mediante la inyección de su bloque de ejecución en elrun()
método.Bueno, muchas buenas respuestas, quiero agregar más sobre esto. Esto ayudará a entender
Extending v/s Implementing Thread
.Extiende los enlaces de dos archivos de clase muy de cerca y puede causar que algunos códigos sean bastante difíciles de manejar.
Ambos enfoques hacen el mismo trabajo pero ha habido algunas diferencias.
La diferencia más común es
Sin embargo, una diferencia significativa entre implementar Runnable y extender Thread es que
by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.
El siguiente ejemplo lo ayudará a comprender más claramente.
Salida del programa anterior.
En el enfoque de interfaz Runnable, solo se está creando una instancia de una clase y ha sido compartida por diferentes subprocesos. Por lo tanto, el valor del contador se incrementa para cada acceso de subproceso.
Mientras que, el enfoque de clase de subproceso, debe crear una instancia separada para cada acceso de subproceso. Por lo tanto, se asigna una memoria diferente para cada instancia de clase y cada una tiene un contador separado, el valor permanece igual, lo que significa que no ocurrirá ningún incremento porque ninguna de las referencias de objeto es igual.
¿Cuándo usar Runnable?
Utilice la interfaz Runnable cuando desee acceder a los mismos recursos del grupo de subprocesos. Evite usar la clase Thread aquí, porque la creación de múltiples objetos consume más memoria y se convierte en una gran sobrecarga de rendimiento.
Una clase que implementa Runnable no es un subproceso y solo una clase. Para que un Runnable se convierta en un subproceso, debe crear una instancia de subproceso y pasarse como objetivo.
En la mayoría de los casos, la interfaz Runnable debe usarse si solo planea anular el
run()
método y no otros métodos Thread. Esto es importante porque las clases no deben subclasificarse a menos que el programador tenga la intención de modificar o mejorar el comportamiento fundamental de la clase.Cuando existe la necesidad de extender una superclase, implementar la interfaz Runnable es más apropiado que usar la clase Thread. Porque podemos extender otra clase mientras implementamos la interfaz Runnable para hacer un hilo.
¡Espero que esto sea de ayuda!
fuente
ExtendsThread et = new ExtendsThread();
Thread tc1 = new Thread(et);
tc1.start();
Thread.sleep(1000);
Thread tc2 = new Thread(et);
tc2.start();
Thread.sleep(1000);
Thread tc3 = new Thread(et);
tc3.start();
¿Está más claro?Thread
incrementado, ¿la afirmación esby extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance
incorrecta? Si no es así, ¿cuál es el caso que demuestra esto?Una cosa que me sorprende aún no se ha mencionado es que la implementación
Runnable
hace que su clase sea más flexible.Si extiende el hilo, la acción que está haciendo siempre será en un hilo. Sin embargo, si lo implementa
Runnable
no tiene que ser así. Puede ejecutarlo en un subproceso, o pasarlo a algún tipo de servicio ejecutor, o simplemente pasarlo como una tarea dentro de una sola aplicación de subprocesos (tal vez se ejecute en un momento posterior, pero dentro del mismo subproceso). Las opciones son mucho más abiertas si solo las usasRunnable
que si te unes a ellasThread
.fuente
Thread
objeto porqueThread implements Runnable
... ;-) ¡Pero "se siente mejor" haciendo estas cosas con aRunnable
que haciéndolas con aThread
!Thread
agrega muchas cosas adicionales que no necesita, y en muchos casos no desea. Siempre es mejor implementar la interfaz que coincide con lo que realmente está haciendo.Si desea implementar o extender cualquier otra clase, la
Runnable
interfaz es más preferible; de lo contrario, si no desea que ninguna otra clase extienda o implemente, entoncesThread
es preferible la clase.La diferencia más común es
Cuando
extends Thread
clasificas, después de eso no puedes extender ninguna otra clase que requieras. (Como sabes, Java no permite heredar más de una clase).Cuando lo haga
implements Runnable
, puede ahorrar espacio para que su clase extienda cualquier otra clase en el futuro o ahora.Java no admite múltiples herencias, lo que significa que solo puede extender una clase en Java, por lo que una vez que extendió la clase Thread perdió su oportunidad y no puede extender o heredar otra clase en Java.
En la programación orientada a objetos, extender una clase generalmente significa agregar nueva funcionalidad y modificar o mejorar comportamientos. Si no estamos haciendo ninguna modificación en Thread, use la interfaz Runnable en su lugar.
La interfaz ejecutable representa una tarea que puede ser ejecutada por Thread o Executors o por cualquier otro medio. Por lo tanto, la separación lógica de Task como Runnable que Thread es una buena decisión de diseño.
Separar la tarea como Ejecutable significa que podemos reutilizar la tarea y también tenemos la libertad de ejecutarla desde diferentes medios. ya que no puede reiniciar un subproceso una vez que se completa. nuevamente Runnable vs Thread para la tarea, Runnable es el ganador.
El diseñador Java reconoce esto y es por eso que los Ejecutores aceptan Runnable como Tarea y tienen un subproceso de trabajo que ejecuta esa tarea.
Heredar todos los métodos de subprocesos son gastos generales adicionales solo para representar una tarea que se puede hacer fácilmente con Runnable.
Cortesía de javarevisited.blogspot.com
Estas fueron algunas de las diferencias notables entre Thread y Runnable en Java. Si conoce otras diferencias en Thread vs Runnable, compártalas a través de comentarios. Personalmente utilizo Runnable sobre Thread para este escenario y recomiendo usar la interfaz Runnable o Callable según sus requisitos.
Sin embargo, la diferencia significativa es.
Cuando
extends Thread
clasifica, cada uno de sus hilos crea un objeto único y se asocia con él. Cuando ustedimplements Runnable
, comparte el mismo objeto con múltiples hilos.fuente
En realidad, no es aconsejable comparar
Runnable
yThread
entre sí.Estos dos tienen una dependencia y relación en subprocesos múltiples al igual que la
Wheel and Engine
relación del vehículo de motor.Yo diría que solo hay una forma de subprocesamiento múltiple con dos pasos. Déjame hacer mi punto.
Ejecutable:
cuando se implementa
interface Runnable
, significa que está creando algo que estárun able
en un hilo diferente. Ahora, crear algo que pueda ejecutarse dentro de un hilo (ejecutable dentro de un hilo), no significa crear un hilo.Entonces la clase
MyRunnable
no es más que una clase ordinaria con unvoid run
método. Y sus objetos serán algunos objetos ordinarios con solo un métodorun
que se ejecutará normalmente cuando se llame. (a menos que pasemos el objeto en un hilo).Thread:
class Thread
diría que es una clase muy especial con la capacidad de iniciar un nuevo Thread que realmente permite multi-threading a través de sustart()
método.¿Por qué no sabio comparar?
Porque los necesitamos a ambos para subprocesos múltiples.
Para Multi-threading necesitamos dos cosas:
Entonces, técnica y teóricamente, ambos son necesarios para iniciar un hilo, uno correrá y otro lo hará correr (como
Wheel and Engine
en un vehículo de motor).Es por eso que no puede iniciar un hilo con el
MyRunnable
que necesita pasarlo a una instancia deThread
.Pero es posible crear y ejecutar un subproceso solo mediante el uso de
class Thread
ClassThread
,Runnable
por lo que todos sabemos queThread
también estáRunnable
dentro.Finalmente
Thread
yRunnable
se complementan entre sí para multihilo no competidor o reemplazo.fuente
ThreadA
ya no tiene sentidoDebe implementar Runnable, pero si está ejecutando en Java 5 o superior, no debe comenzar con,
new Thread
sino usar un ExecutorService . Para obtener detalles, consulte: Cómo implementar subprocesos simples en Java .fuente
No soy un experto, pero puedo pensar en una razón para implementar Runnable en lugar de extender Thread: Java solo admite herencia única, por lo que solo puede extender una clase.
Editar: Esto originalmente decía "Implementar una interfaz requiere menos recursos". también, pero necesita crear una nueva instancia de Thread de cualquier manera, por lo que esto estaba mal.
fuente
Yo diría que hay una tercera forma:
Tal vez esto esté influenciado un poco por mi uso intensivo reciente de Javascript y Actionscript 3, pero de esta manera su clase no necesita implementar una interfaz bastante vaga como
Runnable
.fuente
Con el lanzamiento de Java 8, ahora hay una tercera opción.
Runnable
es una interfaz funcional , lo que significa que se pueden crear instancias con expresiones lambda o referencias de métodos.Su ejemplo puede ser reemplazado por:
o si desea utilizar una
ExecutorService
referencia de método y:Estos no solo son mucho más cortos que sus ejemplos, sino que también vienen con muchas de las ventajas establecidas en otras respuestas
Runnable
sobre el usoThread
, como la responsabilidad individual y el uso de la composición porque no se está especializando en el comportamiento del hilo. De esta manera, también evita crear una clase adicional si todo lo que necesita esRunnable
lo que hace en sus ejemplos.fuente
() -> {}
se supone que representa la lógica personalizada que alguien necesita. ¿Entonces sería mejor decirlo() -> { /* Code here */ }
?Instanciar una interfaz proporciona una separación más limpia entre su código y la implementación de subprocesos, por lo que preferiría implementar Runnable en este caso.
fuente
Todos aquí parecen pensar que implementar Runnable es el camino a seguir y realmente no estoy en desacuerdo con ellos, pero también hay un caso para extender Thread, en mi opinión, de hecho lo has demostrado en tu código.
Si implementa Runnable, entonces la clase que implementa Runnable no tiene control sobre el nombre del subproceso, es el código de llamada que puede establecer el nombre del subproceso, así:
pero si extiende Thread, entonces puede administrar esto dentro de la clase en sí (al igual que en su ejemplo, llama al hilo 'ThreadB'). En este caso usted:
A) podría darle un nombre más útil para fines de depuración
B) están obligando a que ese nombre se use para todas las instancias de esa clase (a menos que ignore el hecho de que es un hilo y haga lo anterior con él como si fuera un Runnable, pero estamos hablando de convenciones aquí, en cualquier caso, también puede ignorar esa posibilidad que siento).
Incluso podría, por ejemplo, tomar un rastro de la pila de su creación y usarlo como el nombre del hilo. Esto puede parecer extraño, pero dependiendo de cómo esté estructurado su código, puede ser muy útil para la depuración.
Esto puede parecer algo pequeño, pero cuando tiene una aplicación muy compleja con muchos subprocesos y de repente las cosas 'se han detenido' (ya sea por un punto muerto o posiblemente debido a una falla en un protocolo de red que sería menos obvio, u otras razones interminables), entonces obtener un volcado de pila de Java donde todos los hilos se llaman 'Thread-1', 'Thread-2', 'Thread-3' no siempre es muy útil (depende de cómo sean sus hilos estructurado y si puede decir de manera útil cuál es cuál solo por su seguimiento de la pila, no siempre es posible si está utilizando grupos de múltiples subprocesos que ejecutan el mismo código).
Dicho esto, por supuesto, también puede hacer lo anterior de manera genérica creando una extensión de la clase de subproceso que establece su nombre en un seguimiento de la pila de su llamada de creación y luego usarlo con sus implementaciones Runnable en lugar de la clase estándar de subprocesos de Java (consulte a continuación), pero además del seguimiento de la pila, puede haber más información específica del contexto que sería útil en el nombre del hilo para la depuración (una referencia a una de las muchas colas o sockets que podría procesar, por ejemplo, en cuyo caso puede preferir extienda Thread específicamente para ese caso para que el compilador lo obligue (u otros que usan sus bibliotecas) a pasar cierta información (por ejemplo, la cola / socket en cuestión) para usar en el nombre).
Aquí hay un ejemplo del hilo genérico con el seguimiento de la pila de llamadas como su nombre:
y aquí hay una muestra de la salida que compara los dos nombres:
fuente
Runnable
puede controlar el nombre del hilo, ya que el hilo que ejecuta el código es, por definición, el hilo actual (y cualquier código que pase las comprobaciones de seguridad tiene control sobre los nombres de los hilos). Teniendo en cuenta que dedicas la mitad de tu publicación a "¡Dios mío, qué pasa con los nombres de los hilos!", Eso parece un gran problema.Ejecutable porque:
Incluso si no necesita nada de esto ahora, puede que lo haga en el futuro. Como no hay ningún beneficio en anular Thread, Runnable es una mejor solución.
fuente
Dado que este es un tema muy popular y las buenas respuestas se extienden por todas partes y se tratan con gran profundidad, sentí que es justificable recopilar las buenas respuestas de los demás en una forma más concisa, por lo que los recién llegados tienen una visión general fácil por adelantado:
Por lo general, extiende una clase para agregar o modificar la funcionalidad. Por lo tanto, si usted no quiere a sobrescribir cualquier comportamiento del hilo , a continuación, utilizar Ejecutable.
En la misma luz, si no es necesario para heredar los métodos de rosca, se puede hacer sin que sobrecarga utilizando Ejecutable.
Herencia única : si extiende Thread no puede extenderlo desde ninguna otra clase, por lo que si eso es lo que necesita hacer, debe usar Runnable.
Es un buen diseño separar la lógica de dominio de los medios técnicos, en ese sentido es mejor tener una tarea Ejecutable que aísle su tarea de su corredor .
Puede ejecutar el mismo objeto Runnable varias veces , sin embargo, un objeto Thread solo se puede iniciar una vez. (Tal vez la razón por la que los Ejecutores aceptan Runnables, pero no Threads).
Si desarrolla su tarea como Runnable, tiene toda la flexibilidad de cómo usarla ahora y en el futuro . Puede ejecutarlo simultáneamente a través de Ejecutores, pero también a través de Thread. Y aún podría usarlo / llamarlo de manera no simultánea dentro del mismo hilo al igual que cualquier otro tipo / objeto ordinario.
Esto hace que sea también más fácil de separar las tareas de lógica y de concurrencia aspectos en sus pruebas de unidad .
Si está interesado en esta pregunta, también podría estar interesado en la diferencia entre Callable y Runnable .
fuente
Las diferencias entre el subproceso extendido y la ejecución ejecutable son:
fuente
Esto se discute en el tutorial de Oracle Defining and Beginning Thread :
En otras palabras, la implementación
Runnable
funcionará en escenarios en los que su clase extiende una clase que no seaThread
. Java no admite herencia múltiple. Además, la extensiónThread
no será posible cuando se utilicen algunas de las API de administración de subprocesos de alto nivel. El único escenario en el queThread
es preferible extender es en una pequeña aplicación que no estará sujeta a actualizaciones en el futuro. Casi siempre es mejor implementar,Runnable
ya que es más flexible a medida que su proyecto crece. Un cambio de diseño no tendrá un impacto importante ya que puede implementar muchas interfaces en Java, pero solo extender una clase.fuente
La explicación más simple sería mediante la implementación de
Runnable
que podemos asignar el mismo objeto a múltiples subprocesos y cada unoThread
comparte los mismos estados y comportamiento del objeto.Por ejemplo, supongamos que hay dos subprocesos, el subproceso1 pone un número entero en una matriz y el subproceso2 toma enteros de la matriz cuando se llena la matriz. Tenga en cuenta que para que thread2 funcione, necesita saber el estado de la matriz, si thread1 lo ha llenado o no.
La implementación le
Runnable
permite tener esta flexibilidad para compartir el objeto, mientrasextends Thread
que le permite crear nuevos objetos para cada subproceso, por lo tanto, cualquier actualización realizada por thread1 se pierde en thread2.fuente
Si no me equivoco, es más o menos similar a
¿Cuál es la diferencia entre una interfaz y una clase abstracta?
extiende establece la relación " Es A " y la interfaz proporciona la capacidad " Tiene una ".
Prefiere implementos Runnable :
Prefiere " hilo extendido ":
En general, no es necesario anular el comportamiento del subproceso. Por lo tanto, los implementos Runnable se prefieren la mayoría de las veces.
En una nota diferente, el uso de avanzado
ExecutorService
oThreadPoolExecutorService
API proporciona más flexibilidad y control.Echa un vistazo a esta pregunta SE:
ExecutorService vs Casual Thread Spawner
fuente
Separar la clase Thread de la implementación Runnable también evita posibles problemas de sincronización entre el thread y el método run (). Un Runnable separado generalmente brinda mayor flexibilidad en la forma en que se hace referencia y se ejecuta el código ejecutable.
fuente
Esa es la S de SÓLIDO : responsabilidad única.
Un subproceso encarna el contexto de ejecución (como en el contexto de ejecución: marco de pila, id de subproceso, etc.) de la ejecución asincrónica de un fragmento de código. Ese código idealmente debería ser la misma implementación, ya sea síncrona o asíncrona .
Si los agrupa en una sola implementación, le da al objeto resultante dos causas de cambio no relacionadas :
Si el lenguaje que usa admite clases parciales o herencia múltiple, puede segregar cada causa en su propia superclase, pero se reduce a lo mismo que componer los dos objetos, ya que sus conjuntos de características no se superponen. Eso es para la teoría.
En la práctica, en general, un programa no necesita llevar más complejidad de la necesaria. Si tiene un hilo trabajando en una tarea específica, sin cambiar esa tarea, probablemente no tenga sentido hacer que las tareas separen las clases, y su código sigue siendo más simple.
En el contexto de Java , dado que la instalación ya está allí , probablemente sea más fácil comenzar directamente con
Runnable
clases independientes y pasar sus instancias aThread
(oExecutor
) instancias. Una vez acostumbrado a ese patrón, no es más difícil de usar (o incluso leer) que el caso de subproceso ejecutable simple.fuente
Una razón por la que desea implementar una interfaz en lugar de extender una clase base es que ya está extendiendo alguna otra clase. Solo puede extender una clase, pero puede implementar cualquier cantidad de interfaces.
Si extiende Thread, básicamente está evitando que su lógica sea ejecutada por cualquier otro hilo que no sea 'this'. Si solo desea que un hilo ejecute su lógica, es mejor implementar Runnable.
fuente
si usa runnable, puede ahorrar espacio para extenderlo a cualquiera de su otra clase.
fuente
¿Podemos volver a visitar la razón básica por la que queríamos que nuestra clase se comportara como una
Thread
? No hay ninguna razón en absoluto, solo queríamos ejecutar una tarea, muy probablemente en un modo asíncrono, lo que significa que la ejecución de la tarea debe ramificarse desde nuestro hilo principal y el hilo principal si termina antes, puede o no esperar para la ruta ramificada (tarea).Si este es todo el propósito, entonces, ¿dónde veo la necesidad de un hilo especializado? Esto se puede lograr recogiendo un subproceso RAW del conjunto de subprocesos del sistema y asignándole nuestra tarea (puede ser una instancia de nuestra clase) y eso es todo.
Así que obedezcamos el concepto de OOP y escriba una clase del tipo que necesitamos. Hay muchas maneras de hacer las cosas, hacerlo de la manera correcta es importante.
Necesitamos una tarea, así que escriba una definición de tarea que pueda ejecutarse en un subproceso. Entonces usa Runnable.
Recordar siempre
implements
se usa especialmente para impartir un comportamiento yextends
se usa para impartir una característica / propiedad.No queremos la propiedad del hilo, en cambio queremos que nuestra clase se comporte como una tarea que se puede ejecutar.
fuente
Sí, si llama a ThreadA call, entonces no necesita llamar al método de inicio y el método de ejecución es llamar después de llamar solo a la clase ThreadA. Pero si usa la llamada ThreadB, entonces necesita el hilo de inicio para el método de ejecución de la llamada. Si tienes más ayuda, respóndeme.
fuente
Creo que es más útil usar Runnable por todas las razones mencionadas, pero a veces me gusta extender Thread para poder crear mi propio método de detención de hilos y llamarlo directamente en el hilo que he creado.
fuente
Java no admite herencia múltiple, por lo que si extiende la clase Thread, no se extenderá ninguna otra clase.
Por ejemplo: si crea un applet, debe extender la clase Applet, por lo que aquí la única forma de crear un subproceso es mediante la implementación de la interfaz Runnable
fuente
Runnable
es una interfaz, mientras queThread
es una clase que implementa esta interfaz. Desde el punto de vista del diseño, debe haber una separación clara entre cómo se define una tarea y cómo se ejecuta. El primero es responsabilidad de unaRunnalbe
implementación, y el segundo es trabajo de laThread
clase. En la mayoría de los casos, la implementaciónRunnable
es el camino correcto a seguir.fuente
Diferencia entre Thread y runnable. Si estamos creando Thread usando la clase Thread, entonces Número de hilos igual al número de objetos que creamos. Si estamos creando un subproceso implementando la interfaz ejecutable, entonces podemos usar un solo objeto para crear múltiples subprocesos, por lo que un solo objeto es compartido por múltiples subprocesos, por lo que tomará menos memoria
Entonces, dependiendo del requisito, si nuestros datos no son sensibles. Por lo tanto, se puede compartir entre varios subprocesos, podemos utilizar la interfaz Runnable
fuente
Agregar mis dos centavos aquí: siempre que sea posible usarlo
implements Runnable
. A continuación hay dos advertencias sobre por qué no debe usarextends Thread
sIdealmente, nunca debe extender la clase Thread; La
Thread
clase debe hacersefinal
. Al menos sus métodos comothread.getId()
. Vea esta discusión para un error relacionado con la extensión deThread
s.Aquellos a quienes les gusta resolver acertijos pueden ver otro efecto secundario de extender Thread. El siguiente código imprimirá un código inalcanzable cuando nadie los notifique.
Consulte http://pastebin.com/BjKNNs2G .
fuente