¿Es posible que un hilo se bloquee solo?


¿Es técnicamente posible que un hilo en Java se bloquee a sí mismo?

Me preguntaron esto en una entrevista hace un tiempo, y respondió que no era posible, pero el entrevistador me dijo que es. Desafortunadamente no pude obtener su método sobre cómo lograr este punto muerto.

Esto me hizo pensar y la única situación en la que puedo pensar es en donde se puede hacer que esto suceda es donde se tiene un proceso de servidor RMI que contenía un método que se llama a sí mismo. La línea de el código que llama al método se coloca en un bloque sincronizado.

¿Es eso posible o fue incorrecto el entrevistador?

El código fuente en el que estaba pensando estaba en estas líneas (donde testDeadlock se está ejecutando en un proceso de servidor RMI)

public boolean testDeadlock () throws RemoteException {
    synchronized (this) {
        //Call testDeadlock via RMI loopback            
    }
}
Author: Balder, 2010-08-16

18 answers

Bueno, basado en la definición de:

Un punto muerto es una situación en la que dos o más acciones competidoras están cada una esperando que la otra termine.

Yo diría que la respuesta es no - seguro de que un hilo puede sentarse allí esperando indefinidamente por algo, sin embargo, a menos que dos acciones que compiten están esperando el uno al otro, por definición no es un punto muerto.

A menos que alguien me explique cómo un solo hilo puede estar esperando simultáneamente dos acciones para terminar?

ACTUALIZACIÓN: La única situación posible que se me ocurre es algún tipo de bomba de mensajes, donde un subproceso procesa un mensaje que le pide que espere indefinidamente a que algo suceda, donde de hecho ese algo será procesado por otro mensaje en la bomba de mensajes.

Este escenario (increíblemente artificial) podría ser técnicamente llamado un punto muerto.

 47
Author: Justin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 13:38:55

Depende de lo que quieras decir exactamente con "punto muerto". Por ejemplo, podría fácilmente wait() en un monitor que nada pulsaría... pero no creo que llamaría a eso punto muerto, como tal.

Pensando a lo largo de sus líneas de "método que se llama a sí mismo", si su servidor solo ejecuta un cierto número de subprocesos, todos podrían estar ocupados esperando respuestas del mismo servidor, si eso cuenta. (Ejemplo más simple: el servidor solo usa un subproceso para el procesamiento. Si escribe un manejador de solicitudes que llama al mismo servidor, estará esperando a que el hilo bloqueado termine de manejar la solicitud antes de que pueda servir la misma solicitud...) Esto no es realmente un" bloque sincronizado " tipo de punto muerto, pero es sin duda un peligro a tener en cuenta.

EDITAR: Para aplicar esta respuesta a la definición en las otras, las acciones que compiten aquí serían "completar la solicitud actual" y "manejar la nueva solicitud". Cada acción está esperando a que ocurra la otra.

 17
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 13:27:56

Tal vez quiso decir BLOQUEAR sí mismo, eso es ciertamente demasiado fácil:

synchronized( this )
{
    wait( );
}
 10
Author: Alexander Pogrebnyak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 13:19:56

Tal vez lo que el entrevistador estaba pensando era:

Thread.currentThread().join();

Sin embargo, yo diría que no cuenta como un punto muerto.

 7
Author: finnw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 13:49:30

Actualizar de un bloqueo de lectura a un bloqueo de escritura (tratando de adquirir un bloqueo de escritura mientras mantiene presionado un bloqueo de lectura) resultará en que el hilo se bloquee completamente. ¿Eso es un punto muerto? Tú eres el juez... Pero esa es la forma más fácil de crear el efecto con un solo hilo.

Http://download.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html

 6
Author: sjlee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 14:36:35

Según Wikipedia, "Un punto muerto es una situación en la que dos o más acciones competidoras están cada una esperando que la otra termine, y por lo tanto ninguna de las dos nunca lo hace."

..."En ciencias de la computación, Coffman deadlock se refiere a una condición específica cuando dos o más procesos están esperando el uno al otro para liberar un recurso, o más de dos procesos están esperando recursos en una cadena circular."

Creo que dos o más son palabras clave aquí si te mantienes estricto con definición.

 5
Author: guiding5,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-27 12:16:24

La JVM solo realiza un seguimiento del subproceso local que tiene el monitor, si la clase que llama hace una llamada externa en sí misma, la llamada entrante hace que el subproceso original se bloquee.

Debería ser capaz de ejecutar este código para ilustrar la idea

import java.rmi.*;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.*;

public class DeadlockThreadExample {

    public static interface DeadlockClass extends Remote {
        public void execute() throws RemoteException;
    }

    public static class DeadlockClassImpl extends UnicastRemoteObject implements DeadlockClass {
        private Object lock = new Object();

        public DeadlockClassImpl() throws RemoteException {
            super();
        }

        public void execute() throws RemoteException {
            try {
                System.out.println("execute()::start");

                synchronized (lock) {
                    System.out.println("execute()::Entered Lock");
                    DeadlockClass deadlockClass = (DeadlockClass) Naming.lookup("rmi://localhost/DeadlockClass");
                    deadlockClass.execute();
                }
                System.out.println("execute()::Exited Lock");
            } catch (NotBoundException e) {
                System.out.println(e.getMessage());
            } catch (java.net.MalformedURLException e) {
                System.out.println(e.getMessage());
            }
            System.out.println("execute()::end");
        }
    }

    public static void main(String[] args) throws Exception {
        LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        DeadlockClassImpl deadlockClassImpl = new DeadlockClassImpl();
        Naming.rebind("DeadlockClass", deadlockClassImpl);
        DeadlockClass deadlockClass = (DeadlockClass) Naming.lookup("rmi://localhost/DeadlockClass");
        deadlockClass.execute();
        System.exit(0);
    }
}

La salida del programa se ve como

execute()::start
execute()::Entered Lock
execute()::start

Además, el volcado del hilo también muestra lo siguiente

"main" prio=6 tid=0x00037fb8 nid=0xb80 runnable [0x0007f000..0x0007fc3c]
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:235)
    - locked <0x02fdc568> (a java.io.BufferedInputStream)
    at java.io.DataInputStream.readByte(DataInputStream.java:241)


"RMI TCP Connection(4)-172.17.23.165" daemon prio=6 tid=0x0ad83d30 nid=0x1590 waiting for monitor entry [0x0b3cf000..0x0b3cfce8]
    at DeadlockThreadExample$DeadlockClassImpl.execute(DeadlockThreadExample.java:24)
    - waiting to lock <0x0300a848> (a java.lang.Object)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)


"RMI TCP Connection(2)-172.17.23.165" daemon prio=6 tid=0x0ad74008 nid=0x15f0 runnable [0x0b24f000..0x0b24fbe8] 
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:235)
    - locked <0x02ffb6d8> (a java.io.BufferedInputStream)
    at java.io.DataInputStream.readByte(DataInputStream.java:241)

Lo que indica que el hilo ha logrado bloquearse

 4
Author: Pram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-17 12:20:52

Si bien no he usado Java, he bloqueado una aplicación de un solo hilo antes. IIRC: Rutina A bloqueó una pieza de datos para actualizarla. La rutina B también bloqueó la misma pieza de datos para actualizarla. Debido a los cambios de requisitos, A terminó llamando a B. Oops.

Por supuesto, esto fue solo un error de desarrollo ordinario que cogí la primera vez que intenté ejecutar el código, pero se bloqueó a sí mismo. Yo creo que los bloqueos de este tipo sería posible en cualquier lenguaje que soporta un sistema de archivos.

 2
Author: Loren Pechtel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 13:52:15

La respuesta (de Pram) marcada como correcta no es técnicamente un punto muerto como otros han sugerido. Está bloqueado.

Yo sugeriría en Java, usted puede apoyarse en la definición de Java (que es consistente con la idea de dos hilos). El juez final puede ser la JVM si detecta el punto muerto en sí. Por lo tanto, en el ejemplo de Pram, el hilo mostraría algo como lo siguiente si fuera un punto muerto geniune.

Deadlock detected
=================

"Negotiator-Thread-1":
  waiting to lock Monitor of com.google.code.tempusfugit.concurrency.DeadlockDetectorTest$Cat@ce4a8a
  which is held by "Kidnapper-Thread-0"

"Kidnapper-Thread-0":
  waiting to lock Monitor of com.google.code.tempusfugit.concurrency.DeadlockDetectorTest$Cash@7fc8b2
  which is held by "Negotiator-Thread-1"

Esta detección de punto muerto ha estado disponible para bloqueos desde 1.5 y Lock basados en bloqueos cíclicos desde 1.6.

Un recurso bloqueado continuamente, o al menos algo que está esperando algo que nunca sucederá, se llama livelock. Problemas similares donde los procesos fuera de la VM (por ejemplo) bases de datos deadlocking son totalmente posibles, pero yo diría que no es apropiado para la pregunta.

Estaría interesado en un escrito de cómo el entrevistador afirma que es posible...

En respuesta a su original pregunta, se necesitan dos para bailar el tango y Yo sugeriría que la respuesta de Pram no debería ser marcada como correcta porque no es así! ;) El subproceso RMI que devuelve la llamada puede causar bloqueo, pero se ejecuta en un subproceso diferente (administrado por el servidor RMI) que el de main. Dos hilos están involucrados incluso si el hilo principal no estableció explícitamente otro. No hay bloqueo como lo demuestra la falta de detección en el volcado de subprocesos (o si hace clic en 'detectar bloqueo' en jconsole), sería más preciso descrito como un livelock.

Habiendo dicho todo eso, cualquier discusión en la línea de cada una de estas respuestas sería suficiente para satisfacerme como entrevistador.

 2
Author: Toby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-31 15:26:00

No, porque Java implementa reentrada. Pero por favor no mezcle concurrencia y RMI así. La sincronización en los stubs es algo completamente diferente a los objetos remotos que se sincronizan internamente.

 1
Author: b_erb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 13:34:15

Puedes entrar en un solo bloqueo de hilo con ReentrantReadWriteLock. Los bloqueos de escritura pueden adquirir bloqueos de lectura, pero no al revés. Lo siguiente se bloqueará indefinidamente.

    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    lock.readLock().lock();
    lock.writeLock().lock();
 1
Author: mR_fr0g,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-08 15:45:45

Idealmente, un hilo nunca debería crear un bloqueo por sí mismo usando 'bloqueos sincronizados' a menos que realmente haya un error en la propia JVM como 'supuestamente' notado por algunas personas en versiones anteriores

 0
Author: Gopi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 13:22:52

Aquí hay una manera de que un hilo se bloquee a sí mismo.

public class DeadlockMe
{
    public static void main(String[] args)
    {
        DeadlockThing foo = new DeadlockThing();
        synchronized(foo)
        {
            try
            {
                foo.wait();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
}

El hilo crea una instancia de una clase - cualquier clase y espera en ella. Debido a que el subproceso creó un objeto con ámbito local, no hay forma posible para que ningún otro subproceso notifique al objeto para que lo despierte.

 0
Author: JeremyP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 14:51:58

Escribes un hilo que puede recibir mensajes de otros hilos diciéndole que, por ejemplo, termine. Escribes código en el subproceso para monitorear otros subprocesos y enviarles mensajes de terminación y esperar una respuesta. El hilo se encuentra en la lista, enviar automáticamente un mensaje a terminar y esperar a terminar. Si no fue escrito de una manera que priorizara los mensajes entrantes sobre el estado de espera ...

 0
Author: dwarFish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 15:07:13

Si estiras la definición del término deadlock: un solo hilo puede encontrarse bloqueado en un bloqueo no reentrante que tomó anteriormente.

 0
Author: carlsborg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 17:45:28

Cuando un subproceso entra en el bloque sincronizado, comprueba si el subproceso actual es el propietario del bloqueo, y si lo es, el subproceso simplemente procede sin esperar.

Así que no creo que sea posible.

 0
Author: Denis Tulskiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 18:44:36

Sé que este es un post antiguo. Aquí hay otro ejemplo de cómo podría suceder si su código tiene interacción con recursos externos:

Tengo un subproceso, que abre una conexión de base de datos, inicia una transactionA y comienza la actualización. El mismo hilo, abrir otra conexión, iniciar otra transacciónB. Sin embargo, debido a que transactionA no se ha comprometido todavía, y tiene la tabla de base de datos bloqueada, transactionB pasa a acceder a esta tabla bloqueada, por lo que tiene que esperar

Al final el mismo hilo es bloquear por sí mismo porque abrió más de una conexión de base de datos.


Esto sucedió mucho en la aplicación con la que trabajo porque hay muchos módulos en la aplicación, y un hilo puede ejecutarse a través de muchos métodos. Estos métodos abren sus propias conexiones. Dado que tuvimos diferentes desarrolladores que escribieron su código, es posible que no vean cómo se inicia el llamado a su código, y por lo tanto no pudieron ver las transacciones generales de la base de datos que abrió la aplicación.

 0
Author: dsum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-31 16:57:28

El entrevistador tenía razón. Un hilo puede bloquearse a sí mismo según JCIP. ¿Pero cómo?

En la sección 2.3.2 de la JCIP tenemos el siguiente párrafo sobre la reentrada:

La reentrada facilita la encapsulación del comportamiento de bloqueo, y por lo tanto simplifica el desarrollo del objeto concurrentcode. Sin reentrantlocks, el código de aspecto muy natural en Listing 2.7, en el que una subclase anula una método sincronizado y luego llama al super método de clase, se bloquearía.

El bloqueo de la palabra clave sincronizada es un bloqueo de reentrada por lo que un hilo puede bloquear y desbloquear de una manera anidada, pero si se utiliza un bloqueo no reentrante como el siguiente ejemplo que escribí como prueba. ¡Tendrás un punto muerto! Según JCIP.

public class SelfDeadLock {


    public static class Father{
        volatile protected int n = 0;
        protected Lock ourLock = new Lock();

        public void writeSth(){
            try {
                ourLock.lock();
                n++;
                System.out.println("Father class: " + n);
            } catch (InterruptedException ex) {
                Logger.getLogger(SelfDeadLock.class.getName()).log(Level.SEVERE, null, ex);
            }
            ourLock.unlock();
        }
    }

    public static class Child extends Father{

        @Override
        public void writeSth() {
            try {
                ourLock.lock();
                n++;
                System.out.println("Child class: " + n);
                super.writeSth();
            } catch (InterruptedException ex) {
                Logger.getLogger(SelfDeadLock.class.getName()).log(Level.SEVERE, null, ex);
            }
            ourLock.unlock();
        }   
    }

    public static void main(String[] args) {
        Child child = new Child();
        child.writeSth();
    }
}
 0
Author: Johnny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-29 03:12:45