Tengo algunas preguntas sobre el uso y la importancia de la synchronized
palabra clave.
- ¿Cuál es el significado de la
synchronized
palabra clave? - ¿Cuándo deberían ser los métodos
synchronized
? - ¿Qué significa programática y lógicamente?
Tengo algunas preguntas sobre el uso y la importancia de la synchronized
palabra clave.
synchronized
palabra clave?synchronized
?Respuestas:
La synchronized
palabra clave trata sobre 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
Los métodos permiten una estrategia simple para prevenir la interferencia del hilo y los errores de consistencia de la memoria: si un objeto es visible para más de un hilo, todas las lecturas o escrituras en las variables de ese objeto se realizan a través de métodos sincronizados.
En pocas palabras: cuando tienes dos hilos que leen y escriben en el mismo 'recurso', digamos una variable llamada foo
, debes asegurarte de que estos hilos accedan a la variable de forma atómica. Sin la synchronized
palabra clave, es posible que su hilo 1 no vea el cambio hecho en el hilo 2 foo
o, lo que es peor, solo se puede cambiar a medias. Esto no sería lo que lógicamente espera.
Nuevamente, este es un tema no trivial en Java. Para obtener más información, explore los temas aquí sobre SO y las Interwebs sobre:
Siga explorando estos temas hasta que el nombre "Brian Goetz" se asocie permanentemente con el término "concurrencia" en su cerebro.
Bueno, creo que ya 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 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 hilo 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 hilo nuevo no puede llamar a NINGÚN método sincronizado del mismo objeto siempre que el hilo anterior que había adquirido el bloqueo no libera el bloqueo.
Algo similar ocurre cuando se llama a cualquier método estático sincronizado de la clase. El subproceso adquiere el bloqueo asociado con la clase (en este caso, cualquier subproceso sincronizado no estático de una instancia de esa clase puede ser llamado por cualquier subproceso 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 que el subproceso de nivel de clase no se libere por el subproceso que actualmente mantiene el bloqueo.
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 sincronizado
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
synchronized
, pero se ignora la coherencia de la memoria.
La synchronized
palabra clave evita el acceso concurrente a un bloque de código u objeto por múltiples hilos. Todos los métodos de Hashtable
son synchronized
, por lo que solo un subproceso puede ejecutar cualquiera de ellos a la vez.
Cuando utilice no synchronized
construcciones como HashMap
, debe crear características de seguridad de subprocesos en su código para evitar errores de coherencia.
synchronized
significa que en un entorno de subprocesos múltiples, un objeto que tiene synchronized
método (s) / bloque (s) no permite que dos subprocesos accedan al synchronized
método (s) / 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 consistencia garantizada de los datos.
Sin embargo, si su aplicación es de un solo subproceso, los synchronized
bloques no proporcionan beneficios.
La synchronized
palabra clave hace que un subproceso obtenga un bloqueo al ingresar el método, por lo que solo un subproceso puede ejecutar el método al mismo tiempo (para la instancia de objeto dada, a menos que sea un método estático).
Esto se llama con frecuencia hacer que la clase sea segura para subprocesos, 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, debido a que se están bloqueando y desbloqueando individualmente, dos hilos desafortunadamente pueden crear un vector con dos elementos.
En efecto, también debe 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 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 una serie de utilidades inteligentes que se ocupan de problemas de subprocesos múltiples.
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 manera segura.
En cuanto a la sintaxis, la synchronized
palabra clave toma un Object
parámetro como es (llamado un objeto de bloqueo ), que luego es seguido por un { block of code }
.
Cuando la ejecución encuentra esta palabra clave, el hilo actual intenta "bloquear / adquirir / poseer" (elija) 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 variables dentro del bloque de código sincronizado será visible para cualquier otro subproceso que ejecute de manera similar el código dentro de un bloque de código sincronizado utilizando el mismo objeto de bloqueo .
Solo un subproceso a la vez puede retener 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.
Adición de synchronized
palabra clave para una definición de método es igual a todo el cuerpo del método de ser envuelto en un bloque de código sincronizado con el objeto de bloqueo ser this
(para los métodos de instancia) y ClassInQuestion.getClass()
(para los métodos de clase) .
- El método de instancia es un método que no tiene static
palabra clave.
- El método de clase es un método que tienestatic
palabra clave.
Sin sincronización, no se garantiza en qué orden 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 hilo y la mitad de los bits escritos por otro hilo, dejando la variable en un estado que ninguno de los hilos intentó escribir, sino un desorden combinado de ambos).
No es suficiente completar una operación de escritura en un hilo antes (tiempo de reloj de pared) de que otro hilo lo lea, porque el hardware podría haber almacenado en caché el valor de la variable, y el hilo de lectura vería el valor almacenado en caché en lugar de lo que se escribió eso.
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 usen para usted bajo los capó.
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 desean 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 campos de referencia de los objetos a los que se refieren. Esta forma de comunicación es extremadamente eficiente, pero hace posibles 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 del hilo y aseguran que los datos sean consistentes. En cualquier momento, solo un subproceso puede acceder a un bloque o método sincronizado ( sección crítica ) mediante la adquisición de un bloqueo. Otros hilos 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 agrega synchronized
a la definición o declaración del método. También puede sincronizar un bloque de código particular con un método.
¿Qué significa programáticamente y lógicamente?
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 otros hilos tendrán que esperar para adquirir un bloqueo. No tienen acceso para ingresar a la sección crítica sin adquirir el 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 qué deben protegerse todas las secciones es responsabilidad del programador.
Más detalles en 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 bloqueo intrínseco o bloqueo de monitor. Los bloqueos intrínsecos desempeñan un papel en ambos aspectos de la sincronización: imponen el acceso exclusivo al estado de un objeto y establecen relaciones antes de que sean esenciales para la visibilidad.
Cada objeto tiene un bloqueo intrínseco asociado . Por convención, un hilo que necesita acceso exclusivo y consistente 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 subproceso posee el bloqueo intrínseco entre el momento en que adquirió el bloqueo y lo liberó. 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 hilo libera un bloqueo intrínseco, se establece una relación antes de que ocurra entre esa acción y cualquier adquisición posterior del mismo bloqueo.
Hacer métodos sincronizados tiene dos efectos :
Primero, 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 se realiza con el objeto.
En segundo lugar, cuando sale un método sincronizado, establece automáticamente una relación de suceso anterior 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:
Synchronized normal method
equivalente a
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 Synchronized statement
(usar clase)
class A {
public static synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(A.class) {
// all function code
}
}
}
Sentencia sincronizada (usando variable)
class A {
private Object lock1 = new Object();
public void methodA() {
synchronized(lock1 ) {
// all function code
}
}
}
Para synchronized
, tenemos ambos Synchronized Methods
y Synchronized Statements
. Sin embargo, Synchronized Methods
es similar a Synchronized Statements
lo que necesitamos entender Synchronized Statements
.
=> Básicamente, tendremos
synchronized(object or class) { // object/class use to provides the intrinsic lock
// code
}
Aquí hay 2 pensamientos que ayudan a entender synchronized
intrinsic lock
asociado con él.synchronized statement
, automáticamente adquiere el intrinsic lock
para ese synchronized statement's
objeto y lo libera cuando el método regresa. Mientras un hilo posea un intrinsic lock
, NINGÚN otro hilo puede adquirir el MISMO bloqueo => hilo seguro.=> Cuando se thread A
invoca synchronized(this){// code 1}
=> todo el código de bloqueo (dentro de la clase) donde have synchronized(this)
y all synchronized normal method
(dentro de la clase) está bloqueado porque el MISMO bloqueo. Se ejecutará después del thread A
desbloqueo ("// código 1" terminado).
Este comportamiento es similar a synchronized(a variable){// code 1}
o synchronized(class)
.
MISMO BLOQUEO => bloqueo (¿no depende de qué método o qué afirmaciones?)
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 ninguna relevancia entre sí, 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 alguna clase, solo hay 1 método sincronizado, o todos los métodos sincronizados en la clase son relevantes entre sí => podemos usar synchronized method
para hacer el código más corto y fácil de entender
(no es relevante para mucho synchronized
, es la diferencia entre objeto y clase o no estático y estático).
synchronized
un método normal synchronized(this)
o synchronized(non-static variable)
se sincronizará la base en cada instancia de objeto. synchronized
método estático synchronized(class)
o synchronized(static variable)
se sincronizará en base a la clasehttps://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
Espero que ayude
Aquí hay una explicación de The Java Tutorials .
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 deSynchronizedCounter
, sincronizar estos métodos tiene dos efectos:
- Primero, 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 se realiza con el objeto.
- En segundo lugar, cuando sale un método sincronizado, establece automáticamente una relación de suceso anterior 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.
A mi entender, sincronizado básicamente significa que el compilador 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 puede escribir un objeto con métodos sincronizados que no sean seguros para subprocesos dependiendo de lo que haga su clase).
Lo que faltan las otras respuestas es un aspecto importante: las barreras de memoria . La sincronización de subprocesos consiste básicamente en dos partes: serialización y visibilidad. Aconsejo a todos que busquen en Google "barrera de memoria jvm", 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, le aconsejo que mire 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 puntos muertos.
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 es necesario dormir (), solo sondea la cola llamando a take ().
En resumen: la "sincronización de memoria" ocurre implícitamente cuando inicia un subproceso, un subproceso finaliza, lee una variable volátil, desbloquea un monitor (deja un bloque / función sincronizado), etc. Esta "sincronización" afecta (en cierto sentido, "se sonroja"). ") todas las escrituras hechas antes de esa acción en particular. En el caso del mencionado 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 ocurren antes de las 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 por lo debido. Y de repente tropezar con este hilo después de que Java no está haciendo lo que se supone que debe hacer en la producción donde hay una carga de trabajo diferente, y es bastante difícil probar los problemas de concurrencia.
Sincronizado simplemente significa que múltiples hilos si están asociados con un solo objeto pueden evitar la lectura y escritura sucias si se usa 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, runnable1 se comparte con el hilo 1 y el hilo 3 y runnable2 se comparte solo con el hilo 2. Ahora, cuando t1 y t3 se inician sin que se use 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, thread 3 esperando que se complete el thread 1 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 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
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 subproceso que intenta acceder primero se bloquea en ese objeto y mientras este bloqueo no esté disponible, ningún otro subproceso puede acceder a ninguno de los métodos / bloques sincronizados de esa instancia de la clase.
Tenga en cuenta que otro hilo puede acceder a un método del mismo objeto que no está definido para sincronizarse. Un hilo puede liberar el bloqueo llamando
Object.wait()
synchronized
El bloque en Java es un monitor en subprocesamiento múltiple. synchronized
El bloque con el mismo objeto / clase se puede ejecutar con un solo hilo, todos los demás están esperando. Puede ayudar con la race condition
situación cuando varios hilos intentan actualizar la misma variable (el primer paso es usar volatile
Acerca de )
Java 5
extendido synchronized
apoyando happens-before
[Acerca de]
Se produce un desbloqueo (bloqueo sincronizado o salida de método) de un monitor antes de cada bloqueo posterior (bloqueo sincronizado o entrada de método) de ese mismo monitor.
El siguiente paso es java.util.concurrent