Pregunta ¿Cuál es el propósito de usar synchronized (Thread.currentThread ()) {...} en Java?


Enfrenté el siguiente código en nuestro proyecto:

synchronized (Thread.currentThread()){
    //some code
}

No entiendo la razón para usar sincronizado en currentThread.

¿Hay alguna diferencia entre

synchronized (Thread.currentThread()){
    //some code
}

Y solo

//some code

¿Puedes dar un ejemplo que muestre la diferencia?

ACTUALIZAR

más en detalles este código de la siguiente manera:

synchronized (Thread.currentThread()) {
       Thread.currentThread().wait(timeInterval);
}

Parece que solo Thread.sleep(timeInterval). ¿Es verdad?


26
2018-04-10 07:40


origen


Respuestas:


considera esto

    Thread t = new Thread() {
        public void run() { // A
            synchronized (Thread.currentThread()) {
                System.out.println("A");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
            }
        }
    };
    t.start();
    synchronized (t) { // B
        System.out.println("B");
        Thread.sleep(5000);
    }

los bloques A y B no pueden ejecutarse simultáneamente, por lo que en la prueba dada, la salida "A" o "B" se retrasará 5 segundos, lo que sucederá primero no está definido


26
2018-04-10 07:53



Aunque esto es casi definitivamente un antipatrón y debe resolverse de manera diferente, su pregunta inmediata aún requiere una respuesta. Si toda su base de código nunca adquiere un bloqueo en ningún Thread instancia distinta de Thread.currentThread(), entonces, de hecho, esta cerradura nunca será contenida. Sin embargo, si en cualquier otro lugar tiene

synchronized (someSpecificThreadInstance) { ... }

entonces tal bloque tendrá que contender con tu bloque mostrado para el mismo bloqueo. De hecho, puede suceder que el hilo llegue synchronized (Thread.currentThread()) debe esperar a que otro hilo renuncie al bloqueo.


10
2018-04-10 07:59



Básicamente no hay diferencia entre la presencia y la ausencia del synchronized bloquear. Sin embargo, puedo pensar en una situación que podría dar otro significado a este uso.

los synchronized bloques tiene un interesante efecto secundario de causar un barrera de memoria para ser creado por el tiempo de ejecución antes de entrar y después de salir del bloque. Una barrera de memoria es una instrucción especial para la CPU que impone todas las variables que se comparten entre múltiples hilos para devolver sus últimos valores. Por lo general, un hilo funciona con su propio dupdo de una variable compartida, y su valor solo es visible para este subproceso. Una barrera de memoria ordena al hilo que actualice el valor de forma tal que el cambio sea visible para los otros hilos.

Entonces, el bloque sincronizado en este caso no bloquea (ya que no habrá ningún caso real de bloqueo y situación de espera, al menos ninguno en lo que pueda pensar)(a menos que el caso de uso mencionado en esta respuesta se trata), pero en su lugar impone los valores de los campos compartidos para devolver su último valor. Esto, sin embargo, es cierto si los otros lugares del código que trabajan con las variables en cuestión también usa barreras de memoria (como tener el mismo synchronized bloquear alrededor de las operaciones de actualización / reasignación). Aún así, esto es no es una solución para evitar las condiciones de carrera.

Si estás interesado, te recomiendo que leas esto artículo. Se trata de barreras de memoria y bloqueo en C # y .NET framework, pero el problema es similar para Java y JVM (excepto para el comportamiento de campos volátiles). Me ayudó mucho a comprender cómo funcionan los hilos, los campos volátiles y los bloqueos en general.

Se deben tener en cuenta algunas consideraciones serias en este enfoque, que se mencionaron en los comentarios a continuación de esta respuesta.

  • La barrera de memoria no implica bloqueo. El acceso seguirá siendo no sincronizado y un sujeto a condiciones de carrera y otros problemas potenciales que uno puede enfrentar El único beneficio es que el hilo puede leer los últimos valores de los campos de memoria compartida, sin el uso de bloqueos. Algunas prácticas utilizan enfoques similares si solo el hilo de trabajo lee de los valores y solo les importa que sean los más actuales, mientras que evitan la sobrecarga de los bloqueos; un caso de uso podría ser un algoritmo de procesamiento de datos simultáneo de alto rendimiento.
  • El enfoque anterior es no fidedigno. Según El comentario de Holger, el compilador podría eliminar las instrucciones de bloqueo al optimizar, ya que podría considerarlas innecesarias. Esto también eliminará las barreras de memoria. El código no emitirá un bloqueo, y no funcionará como se esperaba si se usara un bloqueo, o el propósito era crear una barrera de memoria.
  • El enfoque anterior también es no fidedigno porque el tiempo de ejecución JVM puede eliminar la sincronización cuando puede demostrar que el monitor nunca será adquirido por otro subproceso, lo cual es cierto para este constructo si el código nunca se sincroniza en otro objeto de subproceso que no sea el objeto de subproceso del subproceso actual. Entonces, incluso si funciona durante las pruebas en el sistema A, podría fallar bajo otra JVM en el sistema B. Lo que es peor, el código podría funcionar por un tiempo y luego dejar de funcionar a medida que se apliquen las optimizaciones.
  • Las intenciones del código tal como se mantiene ahora son ambiguas, por lo que uno debe usar medios más explícitos y expresivos para lograr su efecto (ver Comentario de Marko Topolnik para referencia).

7
2018-04-10 07:57



Usted está implementando un mutex recursivo.

es decir, el mismo hilo puede ingresar al bloque de sincronización, pero no a otros hilos.


2
2018-04-10 07:43