sincronizados - synchronizable java
¿Existe alguna ventaja de usar un método sincronizado en lugar de un bloque sincronizado? (22)
¿Puede alguien decirme la ventaja del método sincronizado sobre el bloque sincronizado con un ejemplo?
¿Puede alguien decirme la ventaja del método sincronizado sobre el bloque sincronizado con un ejemplo? Gracias.
No hay una clara ventaja de usar un método sincronizado sobre el bloque.
Quizás el único (pero no lo llamaría una ventaja) es que no necesita incluir la referencia del objeto.
Método:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
Bloquear:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
¿Ver? Ninguna ventaja en absoluto.
Sin embargo, los bloques tienen ventajas sobre los métodos, principalmente en la flexibilidad porque puede usar otro objeto como bloqueo, mientras que sincronizar el método bloquearía todo el objeto.
Comparar:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
contra
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
Además, si el método crece, aún puede mantener la sección sincronizada separada:
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
Método sincronizado
Pros:
- Su IDE puede indicar los métodos sincronizados.
- La sintaxis es más compacta.
- Fuerzas para dividir los bloques sincronizados a métodos separados.
Contras:
- Se sincroniza con esto y, por lo tanto, también permite a los usuarios externos sincronizarse con él.
- Es más difícil mover el código fuera del bloque sincronizado.
Bloque sincronizado
Pros:
- Permite utilizar una variable privada para el bloqueo y, por lo tanto, obligar al bloqueo a permanecer dentro de la clase.
- Los bloques sincronizados se pueden encontrar buscando referencias a la variable.
Contras:
- La sintaxis es más complicada y, por lo tanto, hace que el código sea más difícil de leer.
Personalmente prefiero usar métodos sincronizados con clases enfocadas solo a lo que necesita sincronización. Dicha clase debe ser lo más pequeña posible y, por lo tanto, debería ser fácil revisar la sincronización. Otros no deberían tener que preocuparse por la sincronización.
A menudo, el uso de un bloqueo en un nivel de método es demasiado grosero. Por qué bloquear un fragmento de código que no tiene acceso a ningún recurso compartido al bloquear todo un método. Como cada objeto tiene un bloqueo, puede crear objetos ficticios para implementar la sincronización a nivel de bloque. El nivel de bloqueo es más eficiente porque no bloquea todo el método.
Aqui algunos ejemplos
Nivel de método
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can''t access
}
public void synchronized method2() {
//multiple threads can''t access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
Nivel de bloque
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don''t use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don''t use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
[Editar]
Para la Collection
como Vector
y Hashtable
, se sincronizan cuando ArrayList
o HashMap
no lo están, y necesita configurar una palabra clave sincronizada o invocar el método sincronizado de colecciones:
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
Como cuestión práctica, la ventaja de los métodos sincronizados sobre los bloques sincronizados es que son más resistentes a los idiotas; Debido a que no puede elegir un objeto arbitrario para bloquear, no puede hacer un mal uso de la sintaxis del método sincronizado para hacer cosas estúpidas, como el bloqueo en un literal de cadena o el bloqueo en el contenido de un campo mutable que se modifica desde debajo de los hilos.
Por otro lado, con los métodos sincronizados no puede proteger el bloqueo para que no sea adquirido por ningún hilo que pueda obtener una referencia al objeto.
Por lo tanto, utilizar el sincronizado como modificador de los métodos es mejor para proteger a sus oradores de vacas de hacerse daño, mientras que el uso de bloques sincronizados junto con objetos privados de bloqueo final es mejor para proteger su propio código de los orkos de vacas.
Como ya se dijo aquí, el bloque sincronizado puede usar una variable definida por el usuario como objeto de bloqueo, cuando la función sincronizada usa solo "esto". Y, por supuesto, puede manipular áreas de su función que deberían estar sincronizadas. Pero todos dicen que no hay diferencia entre la función sincronizada y el bloque que cubre toda la función utilizando "esto" como objeto de bloqueo. Eso no es cierto, la diferencia está en el código de bytes que se generará en ambas situaciones. En caso de uso del bloque sincronizado, se debe asignar una variable local que contenga una referencia a "esto". Y, como resultado, tendremos un tamaño un poco más grande para la función (no es relevante si solo tiene un número reducido de funciones).
Puede encontrar una explicación más detallada de la diferencia aquí: http://www.artima.com/insidejvm/ed2/threadsynchP.html
Con los bloques sincronizados, puede tener múltiples sincronizadores, de modo que varias cosas simultáneas pero no conflictivas pueden continuar al mismo tiempo.
Cuando el compilador java convierte su código fuente en código byte, maneja los métodos sincronizados y los bloques sincronizados de manera muy diferente.
Cuando la JVM ejecuta un método sincronizado, el subproceso en ejecución identifica que la estructura method_info del método tiene establecido el indicador ACC_SYNCHRONIZED, luego adquiere el bloqueo del objeto, llama al método y libera el bloqueo. Si ocurre una excepción, el hilo libera automáticamente el bloqueo.
Sin embargo, la sincronización de un bloque de método omite el soporte integrado de la JVM para adquirir el bloqueo de un objeto y el manejo de excepciones y requiere que la funcionalidad se escriba explícitamente en el código de bytes. Si lee el código de bytes de un método con un bloque sincronizado, verá más de una docena de operaciones adicionales para administrar esta funcionalidad.
Esto muestra llamadas para generar tanto un método sincronizado como un bloque sincronizado:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
El método synchronizedMethodGet()
genera el siguiente código de byte:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
Y aquí está el código de byte del método synchronizedBlockGet()
:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
Una diferencia significativa entre el método sincronizado y el bloque es que, el bloque sincronizado generalmente reduce el alcance del bloqueo. Como el alcance del bloqueo es inversamente proporcional al rendimiento, siempre es mejor bloquear solo la sección crítica del código. Uno de los mejores ejemplos del uso de bloque sincronizado es el bloqueo doble control en el patrón Singleton, donde en lugar de bloquear el método getInstance()
completo, solo bloqueamos la sección crítica del código que se usa para crear la instancia de Singleton. Esto mejora el rendimiento drásticamente porque el bloqueo solo se requiere una o dos veces.
Al utilizar métodos sincronizados, deberá tener mucho cuidado si combina métodos sincronizados estáticos y no sincronizados.
De un resumen de la especificación de Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html
La declaración sincronizada (§14.17) calcula una referencia a un objeto; luego intenta realizar una acción de bloqueo en ese objeto y no continúa hasta que la acción de bloqueo se haya completado con éxito. ...
Un método sincronizado (§8.4.3.5) realiza automáticamente una acción de bloqueo cuando se invoca; su cuerpo no se ejecuta hasta que la acción de bloqueo se haya completado con éxito. Si el método es un método de instancia , bloquea el bloqueo asociado con la instancia para la que se invocó (es decir, el objeto que se conocerá durante la ejecución del cuerpo del método). Si el método es estático , bloquea el bloqueo asociado con el objeto Clase que representa la clase en la que se define el método. ...
Según estas descripciones, diría que la mayoría de las respuestas anteriores son correctas, y un método sincronizado podría ser particularmente útil para los métodos estáticos, donde de otro modo tendría que averiguar cómo obtener el "Objeto de clase que representa la clase en la que se encontraba el método. definido. "
Edición: originalmente pensé que eran citas de la especificación de Java real. Aclaró que esta página es solo un resumen / explicación de la especificación.
El método sincronizado se usa para bloquear todos los objetos. El bloque sincronizado se usa para bloquear objetos específicos
En el caso de métodos sincronizados, el bloqueo se adquirirá en un objeto. Pero si va con el bloqueo sincronizado, tiene la opción de especificar un objeto en el que se adquirirá el bloqueo.
Ejemplo:
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
En general, estos son en su mayoría los mismos que no sean explícitos sobre el monitor del objeto que se está utilizando en comparación con el objeto implícito. Una desventaja de los métodos sincronizados que creo que a veces se pasa por alto es que al usar la referencia "this" para sincronizar, se deja abierta la posibilidad de que se bloqueen objetos externos en el mismo objeto. Eso puede ser un error muy sutil si te topas con él. La sincronización en un objeto explícito interno u otro campo existente puede evitar este problema, encapsulando completamente la sincronización.
La única diferencia real es que un bloque sincronizado puede elegir en qué objeto se sincroniza. Un método sincronizado solo puede usar ''this''
(o la instancia de clase correspondiente para un método de clase sincronizado). Por ejemplo, estos son semánticamente equivalentes:
synchronized void foo() {
...
}
void foo() {
synchronized (this) {
...
}
}
Este último es más flexible, ya que puede competir por el bloqueo asociado de cualquier objeto, a menudo una variable miembro. También es más granular porque podría tener un código concurrente ejecutándose antes y después del bloqueo pero aún dentro del método. Por supuesto, usted podría utilizar un método sincronizado con la misma facilidad al refactorizar el código concurrente en métodos no sincronizados separados. Use lo que haga que el código sea más comprensible.
La única diferencia: los bloques sincronizados permiten el bloqueo granular a diferencia del método sincronizado
Básicamente, synchronized
bloques o métodos synchronized
se han utilizado para escribir código seguro para subprocesos al evitar errores de inconsistencia de memoria.
Esta pregunta es muy antigua y muchas cosas han cambiado en los últimos 7 años. Se han introducido nuevas construcciones de programación para la seguridad de subprocesos.
Puede lograr la seguridad de subprocesos utilizando API de concurrencia avanzada en lugar de bloques synchronied
. Esta page documentación proporciona buenas construcciones de programación para lograr la seguridad de subprocesos.
Los objetos de bloqueo admiten lenguajes de bloqueo que simplifican muchas aplicaciones concurrentes.
Executors definen una API de alto nivel para iniciar y administrar subprocesos. Las implementaciones de ejecutor proporcionadas por java.util.concurrent proporcionan administración de agrupación de subprocesos adecuada para aplicaciones a gran escala.
Las colecciones concurrentes facilitan la gestión de grandes colecciones de datos y pueden reducir en gran medida la necesidad de sincronización.
Las variables atómicas tienen características que minimizan la sincronización y ayudan a evitar errores de consistencia de la memoria.
ThreadLocalRandom (en JDK 7) proporciona una generación eficiente de números pseudoaleatorios desde múltiples hilos.
Mejor reemplazo para la sincronización es ReentrantLock , que usa Lock
API de Lock
Un Bloqueo de exclusión mutua reentrante con el mismo comportamiento y semántica básicos que el bloqueo de monitor implícito al que se accede mediante métodos y declaraciones sincronizados, pero con capacidades ampliadas.
Ejemplo con cerraduras:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
Consulte también los paquetes java.util.concurrent y java.util.concurrent.atomic para otras construcciones de programación.
Consulte esta pregunta relacionada también:
La mayoría de las veces lo uso para sincronizar el acceso a una lista o mapa, pero no quiero bloquear el acceso a todos los métodos del objeto.
En el siguiente código, un hilo que modifica la lista no bloqueará la espera de un hilo que esté modificando el mapa. Si los métodos estuvieran sincronizados en el objeto, entonces cada método tendría que esperar a pesar de que las modificaciones que están realizando no entrarán en conflicto.
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
La principal diferencia es que si utiliza un bloque sincronizado, puede bloquear un objeto que no sea este, lo que le permite ser mucho más flexible.
Supongamos que tiene una cola de mensajes y múltiples productores y consumidores de mensajes. No queremos que los productores interfieran entre sí, pero los consumidores deberían poder recuperar mensajes sin tener que esperar a los productores. Así que solo creamos un objeto
Object writeLock = new Object();
Y de ahora en adelante, cada vez que un productor quiera agregar un nuevo mensaje, simplemente lo bloquearemos:
synchronized(writeLock){
// do something
}
Así que los consumidores todavía pueden leer, y los productores estarán bloqueados.
Los métodos sincronizados se pueden verificar utilizando la API de reflexión. Esto puede ser útil para probar algunos contratos, como todos los métodos en el modelo están sincronizados .
El siguiente fragmento imprime todos los métodos sincronizados de Hashtable:
for (Method m : Hashtable.class.getMethods()) {
if (Modifier.isSynchronized(m.getModifiers())) {
System.out.println(m);
}
}
Nota importante sobre el uso del bloque sincronizado: ¡cuidado con lo que usa como objeto de bloqueo!
El fragmento de código del usuario 2277816 anterior ilustra este punto en que se utiliza una referencia a un literal de cadena como objeto de bloqueo. Tenga en cuenta que los literales de cadena se internan automáticamente en Java y debe comenzar a ver el problema: cada pieza de código que se sincroniza en el "bloqueo" literal, ¡comparte el mismo bloqueo! Esto puede llevar fácilmente a puntos muertos con piezas de código completamente no relacionadas.
No es solo con los objetos String con los que debes tener cuidado. Los primitivos en caja también son un peligro, ya que los métodos autoboxing y valueOf pueden reutilizar los mismos objetos, dependiendo del valor.
Para obtener más información, consulte: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused
Nota: los métodos y bloques sincronizados estáticos funcionan en el objeto Clase.
public class MyClass {
// locks MyClass.class
public static synchronized void foo() {
// do something
}
// similar
public static void foo() {
synchronized(MyClass.class) {
// do something
}
}
}
Sé que esta es una pregunta antigua, pero con mi rápida lectura de las respuestas aquí, no vi a nadie mencionar que a veces un método synchronized
puede ser el bloqueo incorrecto .
De Java Concurrency In Practice (pág. 72):
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
El código anterior tiene la apariencia de ser seguro para subprocesos. Sin embargo, en realidad no lo es. En este caso, el bloqueo se obtiene en la instancia de la clase. Sin embargo, es posible que la lista sea modificada por otro hilo que no utilice ese método. El enfoque correcto sería utilizar
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
El código anterior bloquearía todos los subprocesos que intentan modificar la lista para que no modifique la lista hasta que el bloque sincronizado se haya completado.
Sincronizando con hilos. 1) NUNCA use sincronizado (esto) en un hilo que no funciona. La sincronización con (esto) utiliza el hilo actual como el objeto de hilo de bloqueo. Dado que cada subproceso es independiente de otros subprocesos, NO hay coordinación de sincronización. 2) Las pruebas de código muestran que en Java 1.6 en una Mac el método de sincronización no funciona. 3) sincronizado (lockObj) donde lockObj es un objeto compartido común de todos los subprocesos que funcionarán en la sincronización. 4) ReenterantLock.lock () y .unlock () funcionan. Vea los tutoriales de Java para esto.
El siguiente código muestra estos puntos. También contiene el Vector seguro para subprocesos que se sustituiría por ArrayList, para mostrar que muchos subprocesos que agregan a un Vector no pierden ninguna información, mientras que lo mismo con un ArrayList puede perder información. 0) El código actual muestra la pérdida de información debido a las condiciones de la carrera A) Comente la línea A marcada actual, y descomente la línea A que se encuentra arriba, luego ejecute, el método pierde datos pero no debería. B) Invierta el paso A, descomente B y // finalice el bloque}. Luego ejecute para ver los resultados sin pérdida de datos C) Comentario B, descomente C. Ejecute, vea la sincronización en (esto) pierde datos, como se esperaba. No tengo tiempo para completar todas las variaciones, espero que esto ayude. Si la sincronización está activada (esto), o si la sincronización del método funciona, indique qué versión de Java y SO probó. Gracias.
import java.util.*;
/** RaceCondition - Shows that when multiple threads compete for resources
thread one may grab the resource expecting to update a particular
area but is removed from the CPU before finishing. Thread one still
points to that resource. Then thread two grabs that resource and
completes the update. Then thread one gets to complete the update,
which over writes thread two''s work.
DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change
2) Uncomment "synchronized(countLock){ }" - see counts work
Synchronized creates a lock on that block of code, no other threads can
execute code within a block that another thread has a lock.
3) Comment ArrayList, unComment Vector - See no loss in collection
Vectors work like ArrayList, but Vectors are "Thread Safe"
May use this code as long as attribution to the author remains intact.
/mf
*/
public class RaceCondition {
private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
// private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)
private String countLock="lock"; // Object use for locking the raceCount
private int raceCount = 0; // simple add 1 to this counter
private int MAX = 10000; // Do this 10,000 times
private int NUM_THREADS = 100; // Create 100 threads
public static void main(String [] args) {
new RaceCondition();
}
public RaceCondition() {
ArrayList<Thread> arT = new ArrayList<Thread>();
// Create thread objects, add them to an array list
for( int i=0; i<NUM_THREADS; i++){
Thread rt = new RaceThread( ); // i );
arT.add( rt );
}
// Start all object at once.
for( Thread rt : arT ){
rt.start();
}
// Wait for all threads to finish before we can print totals created by threads
for( int i=0; i<NUM_THREADS; i++){
try { arT.get(i).join(); }
catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
}
// All threads finished, print the summary information.
// (Try to print this informaiton without the join loop above)
System.out.printf("/nRace condition, should have %,d. Really have %,d in array, and count of %,d./n",
MAX*NUM_THREADS, raceList.size(), raceCount );
System.out.printf("Array lost %,d. Count lost %,d/n",
MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
} // end RaceCondition constructor
class RaceThread extends Thread {
public void run() {
for ( int i=0; i<MAX; i++){
try {
update( i );
} // These catches show when one thread steps on another''s values
catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
catch( OutOfMemoryError oome ) { System.out.print("O"); }
}
}
// so we don''t lose counts, need to synchronize on some object, not primitive
// Created "countLock" to show how this can work.
// Comment out the synchronized and ending {, see that we lose counts.
// public synchronized void update(int i){ // use A
public void update(int i){ // remove this when adding A
// synchronized(countLock){ // or B
// synchronized(this){ // or C
raceCount = raceCount + 1;
raceList.add( i ); // use Vector
// } // end block for B or C
} // end update
} // end RaceThread inner class
} // end RaceCondition outter class
Método sincronizado
Los métodos sincronizados tienen dos efectos.
Primero, 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 (suspensión de ejecución) hasta que el primer subproceso se realiza 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.
Tenga en cuenta que los constructores no se pueden sincronizar; usar la palabra clave sincronizada con un constructor es un error de sintaxis. La sincronización de constructores no tiene sentido, porque solo el hilo que crea un objeto debe tener acceso a él mientras se está construyendo.
Estado de cuenta sincronizado
A diferencia de los métodos sincronizados, las instrucciones sincronizadas deben especificar el objeto que proporciona el bloqueo intrínseco: la mayoría de las veces lo uso para sincronizar el acceso a una lista o mapa, pero no quiero bloquear el acceso a todos los métodos del objeto.
P: Bloqueos intrínsecos y sincronización La sincronización se basa en una entidad interna conocida como bloqueo intrínseco o bloqueo de monitor. (La especificación de la API a menudo se refiere a esta entidad simplemente como un "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 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.
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
Verificación cruzada de diferentes salidas con método sincronizado, bloque y sin sincronización.
TLDR; No use el modificador synchronized
ni la expresión synchronized(myLock){...}
myLock
esté synchronized(myLock){...}
donde myLock
es un campo de instancia final que contiene un objeto privado.
La diferencia entre usar el modificador synchronized
en la declaración del método y la expresión synchronized(..){ }
en el cuerpo del método es la siguiente:
- El modificador
synchronized
especificado en la firma del método.- Es visible en el JavaDoc generado,
- es determinable programáticamente a través de la reflection cuando se prueba un modificador de método para Modifier.SYNCHRONIZED ,
- requiere menos escritura y sangría en comparación con la
synchronized(this) { .... }
, y - (dependiendo de su IDE) es visible en el resumen de la clase y la finalización del código,
- usa
this
objeto como bloqueo cuando se declara en un método no estático o la clase adjunta cuando se declara en un método estático.
- La expresión
synchronized(...){...}
te permite- para sincronizar solamente la ejecución de partes del cuerpo de un método,
- para ser utilizado dentro de un constructor o un bloque de inicialización ( static ),
- para elegir el objeto de bloqueo que controla el acceso sincronizado.
Sin embargo, usar el modificador synchronized
o synchronized(...) {...}
con this
como el objeto de bloqueo (como en synchronized(this) {...}
), tiene la misma desventaja. Ambos usan su propia instancia como el objeto de bloqueo para sincronizar. Esto es peligroso porque no solo el objeto en sí, sino también cualquier otro objeto / código externo que contenga una referencia a ese objeto, también puede usarlo como un bloqueo de sincronización con efectos secundarios potencialmente graves (degradación del rendimiento y deadlocks ).
Por lo tanto, la mejor práctica es no usar el modificador synchronized
ni la expresión synchronized(...)
junto con this
como objeto de bloqueo, sino como un objeto de bloqueo privado para este objeto. Por ejemplo:
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
También puede usar múltiples objetos de bloqueo, pero se debe tener un cuidado especial para garantizar que esto no provoque puntos muertos cuando se use anidado.
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}