variable - synchronized java para que sirve




¿Qué significa 'sincronizado'? (10)

Tengo algunas preguntas sobre el uso y la importancia de la palabra clave synchronized .

  • ¿Cuál es el significado de la palabra clave synchronized ?
  • ¿Cuándo deben synchronized métodos?
  • ¿Qué significa programáticamente y lógicamente?

¿Qué es la palabra clave sincronizada?

Los hilos se comunican principalmente compartiendo el acceso a los campos y los campos de referencia de los objetos se refieren a. Esta forma de comunicación es extremadamente eficiente, pero hace posible dos tipos de errores: interferencia de hilos y errores de consistencia de la memoria . La herramienta necesaria para prevenir estos errores es la sincronización.

Los bloques o métodos sincronizados previenen la interferencia del hilo y aseguran que los datos sean consistentes. En cualquier momento, solo un hilo puede acceder a un bloque o método sincronizado ( sección crítica ) mediante la adquisición de un bloqueo. Otros subprocesos esperarán a que se libere el bloqueo para acceder a la sección crítica .

¿Cuándo se sincronizan los métodos?

Los métodos se sincronizan cuando se agrega synchronized a la definición o declaración del método. También puede sincronizar un bloque particular de código dentro de un método.

¿Qué significa pro gramáticamente y lógicamente?

Esto significa que solo un hilo puede acceder a la sección crítica mediante la adquisición de un bloqueo. A menos que este hilo libere este bloqueo, todos los demás hilos tendrán que esperar para adquirir un bloqueo. No tienen acceso para entrar en la sección crítica sin tener que adquirir un bloqueo.

Esto no se puede hacer con una magia. Es responsabilidad del programador identificar las secciones críticas en la aplicación y protegerlas en consecuencia. Java proporciona un marco para proteger su aplicación, pero dónde y cuáles son las secciones que deben protegerse es responsabilidad del programador.

Más detalles de la page documentación de java page

Cerraduras intrínsecas y sincronización:

La sincronización se basa en una entidad interna conocida como bloqueo intrínseco o bloqueo de monitor. Los bloqueos intrínsecos desempeñan un papel en ambos aspectos de la sincronización: imponer el acceso exclusivo al estado de un objeto y establecer relaciones de suceso antes de que sean esenciales para la visibilidad.

Cada objeto tiene un bloqueo intrínseco asociado a él . Por convención, un subproceso que necesita acceso exclusivo y coherente a los campos de un objeto tiene que adquirir el bloqueo intrínseco del objeto antes de acceder a ellos, y luego liberar el bloqueo intrínseco cuando se hace con ellos.

Se dice que un hilo posee el bloqueo intrínseco entre el momento en que ha adquirido el bloqueo y ha liberado el bloqueo. Mientras un hilo posea un bloqueo intrínseco, ningún otro hilo puede adquirir el mismo bloqueo. El otro hilo se bloqueará cuando intente adquirir el bloqueo.

Cuando un subproceso libera un bloqueo intrínseco, se establece una relación de suceso antes de que entre esa acción y cualquier adquisición posterior del mismo bloqueo.

Hacer métodos sincronizados tiene dos effects :

En primer lugar, no es posible intercalar dos invocaciones de métodos sincronizados en el mismo objeto.

Cuando un subproceso está ejecutando un método sincronizado para un objeto, todos los demás subprocesos que invocan métodos sincronizados para el mismo bloque de objeto (suspender la ejecución) hasta que el primer subproceso finalice con el objeto.

Segundo, cuando un método sincronizado sale, automáticamente establece una relación de suceso antes de cualquier invocación posterior de un método sincronizado para el mismo objeto.

Esto garantiza que los cambios en el estado del objeto sean visibles para todos los subprocesos.

Busca otras alternativas de sincronización en:

¿Evitar sincronizar (esto) en Java?


Visión general

La palabra clave sincronizada en Java tiene que ver con la seguridad de subprocesos, es decir, cuando varios subprocesos leen o escriben la misma variable.
Esto puede suceder directamente (al acceder a la misma variable) o indirectamente (al usar una clase que usa otra clase que accede a la misma variable).

La palabra clave sincronizada se utiliza para definir un bloque de código en el que varios subprocesos pueden acceder a la misma variable de forma segura.

Más adentro

Sintaxis, la palabra clave synchronized toma un Object como su parámetro (llamado objeto de bloqueo ), que luego es seguido por un { block of code } .

  • Cuando la ejecución se encuentra con esta palabra clave, el subproceso actual intenta "bloquear / adquirir / poseer" (elegir) el objeto de bloqueo y ejecutar el bloque de código asociado después de que se haya adquirido el bloqueo.

  • Se garantiza que cualquier escritura en las variables dentro del bloque de código sincronizado será visible para todas las demás hebras que ejecutan de manera similar el código dentro de un bloque de código sincronizado utilizando el mismo objeto de bloqueo .

  • Solo un hilo a la vez puede mantener el bloqueo, durante el cual todos los demás subprocesos que intentan adquirir el mismo objeto de bloqueo esperarán (pausarán su ejecución). El bloqueo se liberará cuando la ejecución salga del bloque de código sincronizado.

Métodos sincronizados:

Agregar una palabra clave synchronized a una definición de método es igual a todo el cuerpo del método envuelto en un bloque de código sincronizado con el objeto de bloqueo como this (por ejemplo, métodos) y ClassInQuestion.getClass() (para métodos de clase) .

- El método de instancia es un método que no tiene una palabra clave static .
- El método de clase es un método que tiene una palabra clave static .

Técnico

Sin la sincronización, no se garantiza en qué orden se realizan las lecturas y escrituras, posiblemente dejando la variable con basura.
(Por ejemplo, una variable podría terminar con la mitad de los bits escritos por un subproceso y la mitad de los bits escritos por otro subproceso, dejando la variable en un estado que ninguno de los subprocesos trató de escribir, sino un lío combinado de ambos).

No es suficiente completar una operación de escritura en un subproceso antes (tiempo de reloj de pared) otro subproceso lo lee, porque el hardware podría haber almacenado en caché el valor de la variable, y el subproceso de lectura vería el valor almacenado en caché en lugar de lo que se escribió. eso.

Conclusión

Por lo tanto, en el caso de Java, debe seguir el modelo de memoria de Java para asegurarse de que no se produzcan errores de subprocesamiento.
En otras palabras: utilice la sincronización, las operaciones atómicas o las clases que los utilizan para usted debajo de los capítulos.

Fuentes

http://docs.oracle.com/javase/specs/jls/se8/html/index.html
Especificación de lenguaje Java®, 2015-02-13


Bueno, creo que ya tenemos suficientes explicaciones teóricas, así que considera este código.

public class SOP {
    public static void print(String s) {
        System.out.println(s+"\n");
    }
}

public class TestThread extends Thread {
    String name;
    TheDemo theDemo;
    public TestThread(String name,TheDemo theDemo) {
        this.theDemo = theDemo;
        this.name = name;
        start();
    }
    @Override
    public void run() {
        theDemo.test(name);
    }
}

public class TheDemo {
    public synchronized void test(String name) {
        for(int i=0;i<10;i++) {
            SOP.print(name + " :: "+i);
            try{
                Thread.sleep(500);
            } catch (Exception e) {
                SOP.print(e.getMessage());
            }
        }
    }
    public static void main(String[] args) {
        TheDemo theDemo = new TheDemo();
        new TestThread("THREAD 1",theDemo);
        new TestThread("THREAD 2",theDemo);
        new TestThread("THREAD 3",theDemo);
    }
}

Nota: synchronized bloquea la llamada del siguiente hilo al método de prueba () siempre y cuando la ejecución del hilo anterior no haya finalizado. Los hilos pueden acceder a este método uno a la vez. Sin synchronized todos los hilos pueden acceder a este método simultáneamente.

Cuando un subproceso llama al método sincronizado "prueba" del objeto (aquí el objeto es una instancia de la clase "TheDemo") adquiere el bloqueo de ese objeto, cualquier nuevo subproceso no puede llamar CUALQUIER método sincronizado del mismo objeto siempre que el subproceso anterior El que había adquirido la cerradura no libera la cerradura.

Algo similar sucede cuando se llama a cualquier método sincronizado estático de la clase. El subproceso adquiere el bloqueo asociado con la clase (en este caso, cualquier subproceso puede llamar a cualquier método sincronizado no estático de una clase de esa clase porque ese bloqueo de nivel de objeto todavía está disponible). Cualquier otro subproceso no podrá llamar a ningún método estático sincronizado de la clase siempre y cuando el bloqueo que actualmente mantiene el bloqueo no libere el bloqueo de nivel de clase.

Salida con sincronizado

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9

Salida sin sincronizar

THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9

La palabra clave synchronized hace que un subproceso obtenga un bloqueo al ingresar el método, de modo que solo un subproceso puede ejecutar el método al mismo tiempo (para la instancia del objeto dado, a menos que sea un método estático).

Con frecuencia esto se llama hacer que la clase sea segura para los hilos, pero yo diría que esto es un eufemismo. Si bien es cierto que la sincronización protege el estado interno del Vector para que no se corrompa, esto generalmente no ayuda mucho al usuario de Vector.

Considera esto:

 if (vector.isEmpty()){
     vector.add(data);
 }

A pesar de que los métodos involucrados están sincronizados, ya que se están bloqueando y desbloqueando individualmente, dos hilos temporizados desafortunadamente pueden crear un vector con dos elementos.

Entonces, en efecto, también tiene que sincronizar el código de su aplicación.

Debido a que la sincronización a nivel de método es a) costosa cuando no la necesita y b) insuficiente cuando se necesita sincronización, ahora hay reemplazos no sincronizados (ArrayList en el caso de Vector).

Más recientemente, se ha lanzado el paquete de concurrencia, con una serie de utilidades inteligentes que se ocupan de los problemas de subprocesos múltiples.


La palabra clave synchronized tiene que ver con diferentes subprocesos que leen y escriben en las mismas variables, objetos y recursos. Este no es un tema trivial en Java, pero aquí hay una cita de Sun:

synchronized métodos synchronized permiten una estrategia simple para prevenir la interferencia de subprocesos y los errores de consistencia de la memoria: si un objeto es visible a más de un subproceso, todas las lecturas o escrituras en las variables de ese objeto se realizan a través de métodos sincronizados.

En pocas palabras, muy pequeña: cuando tiene dos subprocesos que se leen y escriben en el mismo 'recurso', digamos una variable llamada foo , debe asegurarse de que estos subprocesos accedan a la variable de forma atómica. Sin la palabra clave synchronized , su hilo 1 puede no ver el cambio del hilo 2 hecho a foo , o peor, solo se puede cambiar a la mitad. Esto no sería lo que lógicamente esperas.

De nuevo, este es un tema no trivial en Java. Para obtener más información, explore los temas aquí en SO y los Interwebs sobre:

Continúe explorando estos temas hasta que el nombre "Brian Goetz" se asocie permanentemente con el término "concurrencia" en su cerebro.


Lo que faltan las otras respuestas es un aspecto importante: las barreras de la memoria . La sincronización de hilos consta básicamente de dos partes: serialización y visibilidad. Aconsejo a todos que busquen "barrera de memoria jvm" en Google, ya que es un tema no trivial y extremadamente importante (si modifica los datos compartidos a los que acceden varios subprocesos). Una vez hecho esto, aconsejo mirar las clases del paquete java.util.concurrent que ayudan a evitar el uso de la sincronización explícita, lo que a su vez ayuda a mantener los programas simples y eficientes, tal vez incluso evitando los interbloqueos.

Un ejemplo de ello es ConcurrentLinkedDeque . Junto con el patrón de comando , permite crear subprocesos de trabajo altamente eficientes al rellenar los comandos en la cola concurrente; no se necesita sincronización explícita, no hay puntos muertos posibles, no se requiere explícitamente sleep (), solo sondee la cola llamando a take ().

En resumen: la "sincronización de la memoria" ocurre implícitamente cuando inicia un hilo, un hilo finaliza, lee una variable volátil, desbloquea un monitor (deja un bloque / función sincronizada), etc. Esta "sincronización" afecta (en cierto sentido, "flushes" ") todas las escrituras hechas antes de esa acción particular. En el caso de la mencionada ConcurrentLinkedDeque , la documentación "dice":

Efectos de coherencia de la memoria: al igual que con otras colecciones concurrentes, las acciones en un subproceso antes de colocar un objeto en un ConcurrentLinkedDeque happen-before acciones posteriores al acceso o eliminación de ese elemento del ConcurrentLinkedDeque en otro subproceso.

Este comportamiento implícito es un aspecto un tanto pernicioso porque la mayoría de los programadores de Java sin mucha experiencia solo tomarán mucho lo que se les debe. Y luego, de repente, tropieza con este hilo después de que Java no hace lo que "se supone" debe hacer en la producción donde hay una carga de trabajo diferente, y es bastante difícil probar los problemas de concurrencia.


Sé que ya has recibido tu respuesta.
Escribo esto, solo para ayudar a las personas que tienen la misma pregunta y están buscando una respuesta en esta página.
Aquí hay una explicación de la effects :

Considere el siguiente código:

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

Si count es una instancia de SynchronizedCounter , la SynchronizedCounter estos métodos tiene dos efectos:

  • En primer lugar, no es posible intercalar dos invocaciones de métodos sincronizados en el mismo objeto. Cuando un subproceso está ejecutando un método sincronizado para un objeto, todos los demás subprocesos que invocan métodos sincronizados para el mismo bloque de objeto (suspender la ejecución) hasta que el primer subproceso finalice con el objeto.
  • Segundo, cuando un método sincronizado sale, automáticamente establece una relación de suceso antes de cualquier invocación posterior de un método sincronizado para el mismo objeto. Esto garantiza que los cambios en el estado del objeto sean visibles para todos los subprocesos.

Sincronizado simplemente significa que varios subprocesos si se asocian con un solo objeto pueden evitar la lectura y escritura sucias si se utiliza un bloque sincronizado en un objeto en particular. Para darle más claridad, tomemos un ejemplo:

class MyRunnable implements Runnable {
    int var = 10;
    @Override
    public void run() {
        call();
    }

    public void call() {
        synchronized (this) {
            for (int i = 0; i < 4; i++) {
                var++;
                System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
            }
        }
    }
}

public class MutlipleThreadsRunnable {
    public static void main(String[] args) {
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        Thread t1 = new Thread(runnable1);
        t1.setName("Thread -1");
        Thread t2 = new Thread(runnable2);
        t2.setName("Thread -2");
        Thread t3 = new Thread(runnable1);
        t3.setName("Thread -3");
        t1.start();
        t2.start();
        t3.start();
    }
}

Hemos creado dos objetos de clase MyRunnable, siendo runnable1 compartido con el subproceso 1 y subproceso 3 y runnable2 siendo compartido solo con el subproceso 2. Ahora, cuando t1 y t3 se inician sin el uso sincronizado, la salida PFB sugiere que ambos subprocesos 1 y 3 afectan simultáneamente el valor de var donde para el subproceso 2, var tiene su propia memoria.

Without Synchronized keyword

    Current Thread Thread -1 var value 11
    Current Thread Thread -2 var value 11
    Current Thread Thread -2 var value 12
    Current Thread Thread -2 var value 13
    Current Thread Thread -2 var value 14
    Current Thread Thread -1 var value 12
    Current Thread Thread -3 var value 13
    Current Thread Thread -3 var value 15
    Current Thread Thread -1 var value 14
    Current Thread Thread -1 var value 17
    Current Thread Thread -3 var value 16
    Current Thread Thread -3 var value 18

Usando Synchronzied, el hilo 3 espera que el hilo 1 se complete en todos los escenarios. Hay dos bloqueos adquiridos, uno en runnable1 compartido por el hilo 1 y el hilo 3 y otro en runnable2 compartido por el hilo 2 solamente.

Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18

sincronizada es una palabra clave en Java que se usa para hacer que suceda antes de la relación en un entorno de subprocesos múltiples para evitar la inconsistencia de la memoria y el error de interferencia de subprocesos.


Synchronized normal method equivalente a una Synchronized statement (use esto)

class A {
    public synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(this) {
             // all function code
        }
    } 
}

Synchronized static method equivalente a una Synchronized statement (clase de uso)

class A {
    public static synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(A.class) {
             // all function code
        }
    } 
}

Sentencia sincronizada (utilizando variable)

class A {
    private Object lock1 = new Object();

    public void methodA() {
        synchronized(lock1 ) {
             // all function code
        }
    } 
}

Para synchronized , tenemos Synchronized Methods Synchronized Statements y Synchronized Statements . Sin embargo, los Synchronized Methods son similares a las Synchronized Statements por lo que solo necesitamos entender las Synchronized Statements .

=> Básicamente, tendremos

synchronized(object or class) { // object/class use to provides the intrinsic lock
   // code 
}

Aquí hay 2 piensan que ayudan a entender synchronized

  • Cada objeto / clase tiene un intrinsic lock asociado a él.
  • Cuando un hilo invoca una synchronized statement , adquiere automáticamente el intrinsic lock para synchronized statement's objeto de esa synchronized statement's y lo libera cuando el método vuelve. Mientras un subproceso posea un intrinsic lock , NINGÚN otro subproceso puede adquirir el MISMO bloqueo => seguro para subprocesos.

=> Cuando un thread A invoca synchronized(this){// code 1} => todo el código de bloque (dentro de la clase) donde se ha synchronized(this) y todo synchronized normal method (dentro de la clase) está bloqueado porque el MISMO bloqueo. Se ejecutará después del thread A desbloqueo ("// código 1" finalizado).

Este comportamiento es similar a synchronized(a variable){// code 1} o synchronized(class) .

MISMO BLOQUEO => bloqueo (¿no depende de qué método o qué declaraciones?)

¿Usar método sincronizado o instrucciones sincronizadas?

Prefiero synchronized statements porque es más extensible. Ejemplo, en el futuro, solo necesita sincronizar una parte del método. Por ejemplo, tiene 2 métodos sincronizados y no tiene ninguno relevante entre sí, sin embargo, cuando un subproceso ejecuta un método, bloqueará el otro método (se puede evitar mediante el uso synchronized(a variable) ).

Sin embargo, aplicar el método sincronizado es simple y el código parece simple. Para algunas clases, solo hay 1 método sincronizado, o todos los métodos sincronizados en la clase son relevantes entre sí => podemos usar el synchronized method para hacer el código más corto y fácil de entender

Nota

(no es relevante para mucho synchronized , es la diferencia entre objeto y clase o no estático y estático).

  • Cuando utilice un método synchronized o normal o synchronized(this) o synchronized(non-static variable) , se sincronizará la base en cada instancia de objeto.
  • Cuando utilice synchronized método synchronized o estático o synchronized(class) o synchronized(static variable) , se sincronizará en base a la clase

Referencia

effects page

Espero te ayude







synchronized