¿Qué significa ‘sincronizado’?

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 deberían synchronized métodos?
  • ¿Qué significa programática y lógicamente?

La palabra clave synchronized tiene que ver con diferentes hilos 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 evitar la interferencia de hilos y los errores de consistencia de la memoria: si un objeto es visible para más de un hilo, todas las lecturas o escrituras de las variables de ese objeto se realizan a través de métodos sincronizados.

En pocas palabras: cuando tienes dos hilos que están leyendo y escribiendo en el mismo ‘recurso’, digamos una variable llamada foo , necesitas asegurarte de que estos hilos accedan a la variable de forma atómica. Sin la palabra clave synchronized , es posible que su subproceso 1 no vea el cambio del subproceso 2 realizado en foo o, lo que es peor, que solo se modifique 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 en Interwebs sobre:

  • Concurrencia
  • Modelo de memoria Java

Siga explorando estos temas hasta que el nombre “Brian Goetz” se asocie permanentemente con el término “concurrencia” en su cerebro.

Bueno, creo que tuvimos 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 subproceso al método test () siempre que la ejecución del subproceso anterior no haya finalizado. Los subprocesos pueden acceder a este método de a uno por vez. Sin synchronized todos los hilos pueden acceder a este método simultáneamente.

Cuando un hilo llama al método sincronizado 'test' del objeto (aquí el objeto es una instancia de la clase 'TheDemo') adquiere el locking de ese objeto, cualquier nuevo hilo no puede invocar CUALQUIER método sincronizado del mismo objeto, siempre que el hilo anterior que había adquirido la cerradura no libera la cerradura.

Algo similar sucede cuando se llama a cualquier método estático sincronizado de la clase. El hilo adquiere el locking asociado a la clase (en este caso, cualquier subproceso no estático sincronizado de una instancia de esa clase puede invocarse mediante cualquier subproceso porque ese locking de nivel de objeto aún está disponible). Cualquier otro hilo no podrá llamar a ningún método estático sincronizado de la clase, siempre que el locking de nivel de clase no sea liberado por el hilo que actualmente contiene el locking.

Salida con sincronización

 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 sincronización

 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 evita el acceso simultáneo a un bloque de código u objeto por múltiples subprocesos. De forma predeterminada, una Hashtable está synchronized , por lo que solo un hilo puede acceder a la tabla a la vez.

En el uso de construcciones non-synchronized como HashMap , debe crear características de seguridad de subprocesos en su código para evitar errores de coherencia de memoria.

synchronized significa que en un entorno de múltiples hilos, un objeto que tiene métodos / bloques synchronized no permite que dos hilos accedan a los métodos synchronized / bloque (es) de código al mismo tiempo. Esto significa que un hilo no puede leer mientras otro hilo lo actualiza.

El segundo hilo esperará hasta que el primer hilo complete su ejecución. La sobrecarga es la velocidad, pero la ventaja es la coherencia garantizada de los datos.

Sin embargo, si su aplicación es de subproceso único, synchronized bloques synchronized no proporcionan beneficios.

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

Esto se llama frecuentemente hacer que la clase sea segura para el hilo, pero diría que es un eufemismo. Si bien es cierto que la sincronización protege el estado interno del Vector de corromperse, esto generalmente no ayuda mucho al usuario de Vector.

Considera esto:

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

Aunque los métodos involucrados están sincronizados, porque están siendo bloqueados y desbloqueados individualmente, dos hilos desafortunadamente temporizados pueden crear un vector con dos elementos.

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

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

Más recientemente, se lanzó el paquete de simultaneidad, con varias utilidades inteligentes que se encargan de los problemas de subprocesos múltiples.

Visión de conjunto

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 (accediendo a la misma variable) o indirectamente (usando una clase que usa otra clase que accede a la misma variable).

La palabra clave sincronizada se usa para definir un bloque de código donde múltiples hilos pueden acceder a la misma variable de forma segura.

Más adentro

En términos de syntax, la palabra clave synchronized toma un Object como su parámetro (llamado objeto de locking ), seguido de un { block of code } .

  • Cuando la ejecución encuentra esta palabra clave, el hilo actual intenta “bloquear / adquirir / poseer” (seleccionar) el objeto de locking y ejecutar el bloque de código asociado después de que se haya adquirido el locking.

  • Cualquier escritura en variables dentro del bloque de código sincronizado está garantizada para ser visible para cada otro hilo que de manera similar ejecute código dentro de un bloque de código sincronizado utilizando el mismo objeto de locking .

  • Solo un hilo a la vez puede contener el locking, durante ese tiempo los otros hilos que intenten adquirir el mismo objeto de locking esperarán (pausará su ejecución). El locking se liberará cuando la ejecución salga del bloque de código sincronizado.

Métodos sincronizados:

Agregar 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 locking siendo 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 palabra clave static .
– El método de clase es un método que tiene una palabra clave static .

Técnico

Sin sincronización, no está garantizado el orden en que ocurren 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 intentó escribir, sino un desorden combinado de ambos).

No es suficiente completar una operación de escritura en un hilo antes (hora del reloj de pared) otro hilo lo lee, porque el hardware podría haber almacenado el valor de la variable en caché y el hilo de lectura vería el valor en caché en lugar de lo escrito 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: use sincronización, operaciones atómicas o clases que los utilicen por usted debajo de las capuchas.

Fuentes

http://docs.oracle.com/javase/specs/jls/se8/html/index.html
Java® Language Specification, 2015-02-13

Piense en ello como una especie de torniquete como el que podría encontrar en un campo de fútbol. Hay vapores paralelos de personas que quieren entrar, pero en el torniquete están “sincronizados”. Solo una persona a la vez puede pasar. Todos aquellos que quieran pasar lo harán, pero pueden tener que esperar hasta que puedan pasar.

¿Cuál es la palabra clave sincronizada?

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

Los bloques o métodos sincronizados evitan la interferencia de hilos y garantizan que los datos sean consistentes. En cualquier punto del tiempo, solo un hilo puede acceder a un bloque o método sincronizado ( sección crítica ) adquiriendo un locking. Otro (s) hilo (s) esperará (n) a que se libere el locking 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 statement del método. También puede sincronizar un bloque de código particular con-en un método.

¿Qué significa pro gtwigtical y lógicamente?

Significa que solo un hilo puede acceder a la sección crítica adquiriendo un locking. A menos que este hilo libere este locking, todos los demás hilos tendrán que esperar para adquirir un locking. No tienen acceso para ingresar a la sección crítica sin adquirir el locking.

Esto no se puede hacer con una magia. Es responsabilidad del progtwigdor 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 qué secciones deben protegerse es responsabilidad del progtwigdor.

Más detalles de la página de documentación de Java

Cerraduras intrínsecas y sincronización:

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

Cada objeto tiene un locking intrínseco asociado a él . Por convención, un hilo que necesita acceso exclusivo y consistente a los campos de un objeto tiene que adquirir el locking intrínseco del objeto antes de acceder a ellos, y luego liberar el locking intrínseco cuando termina con ellos.

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

Cuando un hilo libera un locking intrínseco, se establece una relación de pase previo entre esa acción y cualquier adquisición posterior del mismo locking.

Hacer que los métodos estén sincronizados tiene dos efectos :

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

Cuando un hilo está ejecutando un método sincronizado para un objeto, todos los otros hilos que invocan métodos sincronizados para el mismo bloque de objetos (suspenden la ejecución) hasta que el primer hilo termina con el objeto.

En segundo lugar, cuando sale un método sincronizado, establece automáticamente una relación de pasar antes con 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 hilos.

Busque otras alternativas a la sincronización en:

Evitar sincronizado (esto) en Java?

A mi entender, sincronizado básicamente significa que el comstackdor escribe un monitor.enter y monitor.exit alrededor de su método. Como tal, puede ser seguro para subprocesos dependiendo de cómo se use (lo que quiero decir es que puedes escribir un objeto con métodos sincronizados que no es seguro para las tareas en función de lo que haga tu clase).

Sé que ya has recibido tu respuesta.
Escribo esto solo para ayudar a las personas que tienen la misma pregunta y buscan una respuesta en esta página.
aquí hay una explicación de la documentación de Java :

Considera 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 hilo está ejecutando un método sincronizado para un objeto, todos los otros hilos que invocan métodos sincronizados para el mismo bloque de objetos (suspenden la ejecución) hasta que el primer hilo termina con el objeto.
  • En segundo lugar, cuando sale un método sincronizado, establece automáticamente una relación de pasar antes con 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 hilos.

Synchronized normal method equivalente a la Synchronized statement (usar 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 la 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 } } } 

Instrucción sincronizada (usando la variable)

 class A { private Object lock1 = new Object(); public void methodA() { synchronized(lock1 ) { // all function code } } } 

Para synchronized , tenemos tanto Synchronized Methods Synchronized Statements . Sin embargo, los Synchronized Methods son similares a las Synchronized Statements por lo que solo debemos comprender las Synchronized Statements .

=> Básicamente, tendremos

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

Aquí hay 2 que ayudan a entender synchronized

  • Cada objeto / clase tiene un intrinsic lock asociado.
  • Cuando un hilo invoca una synchronized statement , automáticamente adquiere el intrinsic lock para synchronized statement's objeto de synchronized statement's y lo libera cuando el método retorna. Siempre que un hilo posea un intrinsic lock , NINGÚN otro hilo puede adquirir el MISMO locking => hilo seguro.

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

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

SAME LOCK => lock (no depende de qué método? O de qué afirmaciones?)

¿Usar método sincronizado o declaraciones sincronizadas?

Prefiero synchronized statements porque es más extensible. Ejemplo, en el futuro, solo necesitas sincronizar una parte del método. Por ejemplo, tiene 2 métodos sincronizados y no tiene ninguno relevante el uno al otro, sin embargo, cuando un subproceso ejecuta un método, bloqueará el otro método (puede evitarlo 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 que el código sea más corto y fácil de entender

Nota

(No es relevante mucho para synchronized , es lo diferente entre objeto y clase o ninguno estático y estático).

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

Referencia

https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

Espero que ayude

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

Un ejemplo de esto 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 es necesaria una sincronización explícita, no hay interlockings posibles, no es necesario el sleep () explícito, simplemente sondee la cola llamando a take ().

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

Efectos de coherencia de memoria: al igual que con otras colecciones simultáneas, las acciones en un hilo antes de colocar un objeto en un movimiento ConcurrentLinkedDeque suceden, antes de acciones posteriores al acceso o eliminación de ese elemento del ConcurrentLinkedDeque en otro hilo.

Este comportamiento implícito es un aspecto algo pernicioso porque la mayoría de los progtwigdores de Java sin mucha experiencia solo tomarán mucho dado por eso. Y de repente tropezar con este hilo después de que Java no está haciendo lo que se supone que debe hacer en producción donde hay una carga de trabajo diferente, y es bastante difícil probar los problemas de concurrencia.

Sincronizado simple significa que no hay dos hilos que puedan acceder al bloque / método simultáneamente. Cuando decimos que cualquier bloque / método de una clase está sincronizado, significa que solo un hilo puede acceder a ellos a la vez. Internamente, el hilo que intenta acceder primero bloquea el objeto y mientras este no esté disponible, ningún otro hilo puede acceder a ninguno de los métodos / bloques sincronizados de esa instancia de la clase.

Tenga en cuenta que otro subproceso puede acceder a un método del mismo objeto que no está definido para ser sincronizado. Un hilo puede liberar el locking llamando

 Object.wait() 

Sincronizado simplemente significa que múltiples hilos si están asociados con un solo objeto pueden evitar la lectura y escritura sucias si el bloque sincronizado se usa en un objeto 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 el ejecutable1 el que se comparte con el subproceso 1 y el subproceso 3 y el ejecutable2 siendo compartidos solo con el subproceso 2. Ahora, cuando t1 y t3 comienzan sin que se use sincronización, salida de PFB que sugiere que ambos hilos 1 y 3 afectan simultáneamente el valor de var donde para el hilo 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. Se adquirieron dos lockings, uno en el ejecutable1 compartido por el hilo 1 y el hilo 3 y otro en el ejecutable2 compartido solo por el hilo 2.

 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 

synchronized es una palabra clave en Java que se utiliza para hacer que ocurra antes de la relación en el entorno de subprocesos múltiples para evitar la incoherencia de la memoria y el error de interferencia de subprocesos.