variable tutorialspoint threading thread squad que multi example black java multithreading synchronization synchronized

java - tutorialspoint - thread sync black squad



¿Evitar sincronizar(esto) en Java? (19)

Cada vez que aparece una pregunta en SO acerca de la sincronización de Java, algunas personas están muy ansiosas por señalar que se debe evitar la synchronized(this) . En su lugar, afirman, un bloqueo en una referencia privada es preferible.

Algunas de las razones dadas son:

Otras personas, incluyéndome a mí, argumentan que synchronized(this) es un idioma que se usa mucho (también en las bibliotecas de Java), es seguro y se entiende bien. No debe evitarse porque tiene un error y no tiene idea de lo que está pasando en su programa de multiproceso. En otras palabras: si es aplicable, entonces úselo.

Estoy interesado en ver algunos ejemplos del mundo real (sin cosas de foobar) en los que es preferible evitar un bloqueo en this cuando synchronized(this) también haría el trabajo.

Por lo tanto: ¿siempre debe evitar la synchronized(this) y reemplazarlo con un bloqueo en una referencia privada?

Alguna información adicional (actualizada a medida que se dan las respuestas):

  • estamos hablando de sincronización de instancias
  • Tanto la forma implícita (métodos synchronized ) como la forma explícita de synchronized(this) se consideran
  • Si cita a Bloch u otras autoridades sobre el tema, no omita las partes que no le gustan (por ejemplo, Effective Java, elemento en Thread Safety: por lo general, es el bloqueo de la instancia en sí, pero hay excepciones).
  • Si necesita una granularidad en el bloqueo que no esté synchronized(this) , entonces synchronized(this) no es aplicable, así que ese no es el problema.

  1. Haga su información inmutable si es posible (variables final )
  2. Si no puede evitar la mutación de datos compartidos en varios subprocesos, use construcciones de programación de alto nivel [por ejemplo, API de Lock granular]

Un bloqueo proporciona acceso exclusivo a un recurso compartido: solo un hilo a la vez puede adquirir el bloqueo y todos los accesos al recurso compartido requieren que el bloqueo se adquiera primero.

Código de ejemplo para usar ReentrantLock que implementa Lock interfaz de Lock

class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } }

Ventajas del bloqueo sobre sincronizado (esto)

  1. El uso de métodos o declaraciones sincronizados obliga a que todas las adquisiciones y liberaciones de bloqueo se realicen de forma estructurada por bloques.

  2. Las implementaciones de bloqueo proporcionan funcionalidad adicional sobre el uso de métodos y declaraciones sincronizados al proporcionar

    1. Un intento sin bloqueo de adquirir un bloqueo ( tryLock() )
    2. Un intento de adquirir el bloqueo que puede ser interrumpido ( lockInterruptibly() )
    3. Un intento de adquirir el bloqueo que puede tryLock(long, TimeUnit) ).
  3. Una clase de bloqueo también puede proporcionar un comportamiento y una semántica que son bastante diferentes de los del bloqueo de monitor implícito, como

    1. orden garantizada
    2. uso no entrante
    3. Detección de interbloqueo

Echa un vistazo a esta pregunta de SE con respecto a varios tipos de Locks :

Sincronización vs bloqueo

Puede lograr la seguridad de subprocesos utilizando API de concurrencia avanzada en lugar de bloques sincronizados. 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.

Consulte también los paquetes java.util.concurrent y java.util.concurrent.atomic para otras construcciones de programación.


Bueno, en primer lugar hay que señalar que:

public void blah() { synchronized (this) { // do stuff } }

es semánticamente equivalente a:

public synchronized void blah() { // do stuff }

que es una razón para no usar synchronized(this) . Podría argumentar que puede hacer cosas alrededor del bloque synchronized(this) . El motivo habitual es tratar de evitar tener que realizar la comprobación sincronizada, lo que conduce a todo tipo de problemas de concurrencia, específicamente el problema de doble comprobación de bloqueo , que solo demuestra lo difícil que puede ser realizar una comprobación relativamente simple. a salvo de amenazas.

Una cerradura privada es un mecanismo de defensa, que nunca es una mala idea.

Además, como mencionó, los bloqueos privados pueden controlar la granularidad. Un conjunto de operaciones en un objeto puede no estar totalmente relacionado con otro, pero la synchronized(this) excluirá mutuamente el acceso a todas ellas.

synchronized(this) realmente no te da nada.


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, etc.

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 de la función (no 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

Además, el uso del bloque sincronizado no es bueno debido al siguiente punto de vista:

La palabra clave sincronizada está muy limitada en un área: al salir de un bloque sincronizado, todos los subprocesos que esperan ese bloqueo deben desbloquearse, pero solo uno de ellos puede tomar el bloqueo; todos los demás ven que se toma el bloqueo y vuelven al estado bloqueado. Eso no es solo una gran cantidad de ciclos de procesamiento desperdiciados: a menudo, el cambio de contexto para desbloquear un subproceso también involucra paginar la memoria del disco, y eso es muy, muy costoso.

Para más detalles en esta área, le recomiendo que lea este artículo: http://java.dzone.com/articles/synchronized-considered


Creo que hay una buena explicación de por qué cada una de estas son técnicas vitales en su haber en un libro llamado Java Concurrency In Practice de Brian Goetz. Él deja muy claro un punto: debe usar el mismo candado "EN TODAS PARTES" para proteger el estado de su objeto. El método sincronizado y la sincronización en un objeto a menudo van de la mano. Ej. Vector sincroniza todos sus métodos. Si tiene un identificador de un objeto vectorial y va a hacer "poner si está ausente", simplemente la sincronización de Vector con sus propios métodos individuales no lo protegerá de la corrupción del estado. Necesitas sincronizar usando sincronizado (vectorHandle). Esto resultará en que el MISMO bloqueo sea adquirido por cada subproceso que tiene un controlador para el vector y protegerá el estado general del vector. Esto se llama bloqueo del lado del cliente. Sabemos que, de hecho, el vector sincroniza (esto) / sincroniza todos sus métodos y, por lo tanto, la sincronización en el objeto vectorHandle resultará en una sincronización adecuada del estado de los objetos vectoriales. Es absurdo creer que estás seguro de subprocesos simplemente porque estás utilizando una colección segura de subprocesos. Esta es precisamente la razón por la que ConcurrentHashMap introdujo explícitamente el método putIfAbsent - para hacer que tales operaciones sean atómicas.

En resumen

  1. La sincronización a nivel de método permite el bloqueo del lado del cliente.
  2. Si tiene un objeto de bloqueo privado, hace imposible el bloqueo del lado del cliente. Esto está bien si sabe que su clase no tiene el tipo de funcionalidad "poner si está ausente".
  3. Si está diseñando una biblioteca, entonces la sincronización en esta o la sincronización del método a menudo es más inteligente. Debido a que rara vez está en posición de decidir cómo se va a utilizar su clase.
  4. Si Vector hubiera utilizado un objeto de bloqueo privado, hubiera sido imposible que se "pusiera si estaba ausente". El código del cliente nunca obtendrá un identificador para el bloqueo privado, rompiendo así la regla fundamental de usar el MISMO BLOQUEO EXACTO para proteger su estado.
  5. La sincronización en este método o en los métodos sincronizados tiene un problema, como han señalado otros: alguien podría obtener un bloqueo y nunca liberarlo. Todos los otros hilos seguirían esperando a que se libere el bloqueo.
  6. Así que sabe lo que estás haciendo y adopta el correcto.
  7. Alguien argumentó que tener un objeto de bloqueo privado le brinda una mejor granularidad, por ejemplo, si dos operaciones no están relacionadas, podrían estar protegidos por diferentes bloqueos que resulten en un mejor rendimiento. Pero creo que esto es el olor del diseño y no el olor del código: si dos operaciones no tienen ninguna relación, ¿por qué son parte de la misma clase? ¿Por qué un club de clase no tiene ninguna funcionalidad relacionada? ¿Puede ser una clase de utilidad? Hmmmm - ¿Alguna utilidad que proporciona manipulación de cadenas y formato de fecha de calendario a través de la misma instancia? ¡¡... no tiene ningún sentido para mí al menos !!

Depende de la situación.
Si solo hay una entidad compartida o más de una.

Ver ejemplo de trabajo completo here

Una pequeña introducción.

Hilos y entidades compartibles.
Es posible que varios subprocesos accedan a la misma entidad, por ejemplo, para múltiples subprocesos de conexión que comparten un único messageQueue. Dado que los subprocesos se ejecutan simultáneamente, puede haber una posibilidad de anular los datos de uno por otro, lo que puede ser una situación desordenada.
Por lo tanto, necesitamos alguna forma de garantizar que solo un hilo a la vez acceda a la entidad que se puede compartir. (CONCURRENCIA).

Bloque sincronizado
El bloque sincronizado () es una forma de garantizar el acceso simultáneo de una entidad que se puede compartir.
Primero, una pequeña analogía.
Supongamos que hay dos personas P1, P2 (hilos) un lavabo (entidad que se puede compartir) dentro de un baño y hay una puerta (cerradura).
Ahora queremos que una persona use el lavabo a la vez.
Una aproximación es bloquear la puerta con P1 cuando la puerta está bloqueada, P2 espera hasta que p1 complete su trabajo
P1 abre la puerta
entonces solo p1 puede usar lavabo.

sintaxis.

synchronized(this) { SHARED_ENTITY..... }

"esto" proporcionó el bloqueo intrínseco asociado con la clase (la clase de Objeto diseñada por el desarrollador de Java de tal manera que cada objeto pueda funcionar como monitor). El enfoque anterior funciona bien cuando solo hay una entidad compartida y varios subprocesos (1: N).
N entidades compartibles-M hilos
Ahora piense en una situación en la que haya dos lavabos dentro de un baño y una sola puerta. Si utilizamos el método anterior, solo p1 puede usar un lavabo a la vez, mientras que p2 esperará afuera. Es un desperdicio de recursos ya que nadie está usando B2 (lavabo).
Un enfoque más inteligente sería crear una habitación más pequeña dentro del baño y proporcionarles una puerta por lavabo. De esta manera, P1 puede acceder a B1 y P2 puede acceder a B2 y viceversa.

washbasin1; washbasin2; Object lock1=new Object(); Object lock2=new Object(); synchronized(lock1) { washbasin1; } synchronized(lock2) { washbasin2; }


Ver más en Temas ----> here


El paquete java.util.concurrent ha reducido enormemente la complejidad de mi código seguro para subprocesos. Solo tengo evidencia anecdótica para continuar, pero la mayoría del trabajo que he visto con synchronized(x) parece estar reimplementando un Bloqueo, Semáforo o Latch, pero utilizando los monitores de nivel inferior.

Teniendo esto en cuenta, la sincronización mediante cualquiera de estos mecanismos es análoga a la sincronización en un objeto interno, en lugar de perder un bloqueo. Esto es beneficioso porque tiene la absoluta certeza de que controla la entrada en el monitor mediante dos o más subprocesos.


La razón para no sincronizar en esto es que a veces necesita más de un bloqueo (el segundo bloqueo a menudo se elimina después de algún pensamiento adicional, pero aún lo necesita en el estado intermedio). Si bloquea esto , siempre debe recordar cuál de los dos bloqueos es este ; Si bloquea un objeto privado, el nombre de la variable le dice eso.

Desde el punto de vista del lector, si ve un bloqueo en esto , siempre tiene que responder las dos preguntas:

  1. ¿Qué tipo de acceso está protegido por esto ?
  2. ¿Es una cerradura realmente suficiente, alguien no introdujo un error?

Un ejemplo:

class BadObject { private Something mStuff; synchronized setStuff(Something stuff) { mStuff = stuff; } synchronized getStuff(Something stuff) { return mStuff; } private MyListener myListener = new MyListener() { public void onMyEvent(...) { setStuff(...); } } synchronized void longOperation(MyListener l) { ... l.onMyEvent(...); ... } }

Si dos hilos comienzan longOperation() en dos instancias diferentes de BadObject , adquieren sus bloqueos; cuando es el momento de invocar l.onMyEvent(...) , tenemos un interbloqueo porque ninguno de los subprocesos puede adquirir el bloqueo del otro objeto.

En este ejemplo, podemos eliminar el interbloqueo utilizando dos bloqueos, uno para operaciones cortas y otro para bloqueos largos.


Mientras usa sincronizado (esto), está usando la instancia de clase como un bloqueo en sí mismo. Esto significa que mientras el bloqueo es adquirido por el subproceso 1, el subproceso 2 debe esperar

Supongamos el siguiente código

public void method1() { do something ... synchronized(this) { a ++; } ................ } public void method2() { do something ... synchronized(this) { b ++; } ................ }

El método 1 modifica la variable a y el método 2 modifica la variable b , la modificación concurrente de la misma variable por dos subprocesos debe evitarse y lo es. PERO, mientras que thread1 modifica a y thread2 modifica b, se puede realizar sin ninguna condición de carrera.

Desafortunadamente, el código anterior no lo permitirá ya que estamos usando la misma referencia para un bloqueo; Esto significa que los subprocesos, incluso si no están en una condición de carrera, deben esperar y, obviamente, el código sacrifica la concurrencia del programa.

La solución es utilizar 2 bloqueos diferentes para dos variables diferentes.

class Test { private Object lockA = new Object(); private Object lockB = new Object(); public void method1() { do something ... synchronized(lockA) { a ++; } ................ } public void method2() { do something ... synchronized(lockB) { b ++; } ................ }

El ejemplo anterior utiliza bloqueos de grano más fino (2 bloqueos en lugar de uno ( lockA y lockB para las variables a y b respectivamente) y como resultado permite una mejor concurrencia, por otro lado se volvió más complejo que el primer ejemplo ...


No, no siempre deberías. Sin embargo, tiendo a evitarlo cuando existen múltiples preocupaciones sobre un objeto en particular que solo necesitan ser seguras para subprocesos con respecto a ellos mismos. Por ejemplo, es posible que tenga un objeto de datos mutable que tenga los campos "etiqueta" y "padre"; estos deben ser seguros para subprocesos, pero cambiar uno no debe impedir que el otro se escriba o se lea. (En la práctica, evitaría esto al declarar que los campos son volátiles y / o usar los envoltorios AtomicFoo de java.util.concurrent).

La sincronización en general es un poco torpe, ya que aplasta un gran bloqueo en lugar de pensar exactamente cómo se puede permitir que las hebras trabajen unas con otras. El uso synchronized(this) es aún más torpe y antisocial, ya que dice que "nadie puede cambiar nada en esta clase mientras mantengo el bloqueo". ¿Con qué frecuencia realmente necesitas hacer eso?

Preferiría tener cerraduras más granulares; incluso si desea evitar que todo cambie (tal vez esté serializando el objeto), solo puede adquirir todos los bloqueos para lograr lo mismo, y es más explícito de esa manera. Cuando usa synchronized(this) , no está claro exactamente por qué está sincronizando o cuáles podrían ser los efectos secundarios. Si usa synchronized(labelMonitor) , o incluso mejor labelLock.getWriteLock().lock() , está claro qué está haciendo y a qué se limitan los efectos de su sección crítica.


Se utiliza un bloqueo para la visibilidad o para proteger algunos datos de modificaciones simultáneas que pueden conducir a la carrera.

Cuando solo necesita realizar operaciones de tipo primitivo para que sean atómicas, hay opciones disponibles como AtomicInteger y AtomicInteger .

Pero supongamos que tiene dos enteros relacionados entre sí como las coordenadas x e y , que están relacionados entre sí y deberían cambiarse de forma atómica. Entonces los protegerías usando una misma cerradura.

Un bloqueo solo debe proteger el estado relacionado entre sí. Ni menos ni más. Si usa synchronized(this) en cada método, incluso si el estado de la clase no está relacionado, todos los subprocesos se enfrentarán a la contención, incluso si se actualiza el estado no relacionado.

class Point{ private int x; private int y; public Point(int x, int y){ this.x = x; this.y = y; } //mutating methods should be guarded by same lock public synchronized void changeCoordinates(int x, int y){ this.x = x; this.y = y; } }

En el ejemplo anterior, solo tengo un método que muta tanto x como y y no dos métodos diferentes, ya que x e y están relacionados, y si hubiera dado dos métodos diferentes para mutar x e y separado, no habría sido seguro para subprocesos.

Este ejemplo es solo para demostrar y no necesariamente la forma en que debe implementarse. La mejor manera de hacerlo sería hacerlo INMUTABLE .

Ahora en oposición al ejemplo de Point , hay un ejemplo de TwoCounters ya proporcionado por @Andreas donde el estado que está siendo protegido por dos bloqueos diferentes ya que el estado no está relacionado entre sí.

El proceso de usar diferentes bloqueos para proteger estados no relacionados se llama Bloqueo de franjas de bloqueo o División de bloqueos


Si bien estoy de acuerdo en no adherirme ciegamente a las reglas dogmáticas, ¿el escenario de "robo de la cerradura" te parece tan excéntrico? De hecho, un subproceso podría adquirir el bloqueo en su objeto "externamente" ( synchronized(theObject) {...} ), bloqueando otros subprocesos que esperan en los métodos de instancia sincronizados.

Si no cree en el código malicioso, considere que este código podría provenir de terceros (por ejemplo, si desarrolla algún tipo de servidor de aplicaciones).

La versión "accidental" parece menos probable, pero como dicen, "haga algo a prueba de idiotas y alguien inventará un mejor idiota".

Así que estoy de acuerdo con la escuela de pensamiento que depende de lo que la clase hace.

Editar los siguientes 3 primeros comentarios de eljenso:

Nunca he experimentado el problema del robo de cerraduras, pero aquí hay un escenario imaginario:

Digamos que su sistema es un contenedor de servlets, y el objeto que estamos considerando es la implementación ServletContext . Su método getAttribute debe ser seguro para subprocesos, ya que los atributos de contexto son datos compartidos; Así lo declaras como synchronized . Imaginemos también que proporciona un servicio de alojamiento público basado en la implementación de su contenedor.

Soy su cliente e implemento mi "buen" servlet en su sitio. Sucede que mi código contiene una llamada a getAttribute .

Un pirata informático, disfrazado de otro cliente, implementa su servlet malicioso en su sitio. Contiene el siguiente código en el método init :

synchronized (this.getServletConfig().getServletContext()) { while (true) {} }

Suponiendo que compartimos el mismo contexto de servlet (permitido por la especificación siempre y cuando los dos servlets estén en el mismo host virtual), mi llamada en getAttribute está bloqueada para siempre. El hacker ha logrado un DoS en mi servlet.

Este ataque no es posible si getAttribute está sincronizado en un bloqueo privado, porque el código de terceros no puede adquirir este bloqueo.

Admito que el ejemplo está diseñado y tiene una visión demasiado simplista de cómo funciona un contenedor de servlets, pero en mi humilde opinión lo demuestra.

Por lo tanto, tomaría mi decisión de diseño en función de la seguridad: ¿tendré control total sobre el código que tiene acceso a las instancias? ¿Cuál sería la consecuencia de que un hilo mantenga un bloqueo en una instancia por tiempo indefinido?


Si has decidido que:

  • lo que necesitas hacer es bloquear el objeto actual; y
  • desea bloquearlo con granularidad más pequeña que un método completo;

entonces no veo un tabú sobre synchronizezd (esto).

Algunas personas usan deliberadamente sincronizado (esto) (en lugar de marcar el método sincronizado) dentro de todo el contenido de un método porque piensan que es "más claro para el lector" qué objeto se está sincronizando realmente. Mientras la gente tome una decisión informada (p. Ej., Entienda que, al hacerlo, en realidad están insertando códigos de byte adicionales en el método y esto podría tener repercusiones en las posibles optimizaciones), no veo particularmente ningún problema con esto . Siempre debe documentar el comportamiento concurrente de su programa, por lo que no veo que el argumento "sincronizado publique el comportamiento" sea tan convincente.

En cuanto a la pregunta de qué bloqueo de objeto debería usar, creo que no hay nada de malo en sincronizar el objeto actual si la lógica de lo que está haciendo y cómo se usaría su clase normalmente lo esperaría . Por ejemplo, con una colección, el objeto que lógicamente espera bloquear es generalmente la propia colección.


Voy a cubrir cada punto por separado.

  1. Algún código malvado puede robar su cerradura (muy popular, también tiene una variante "accidental")

    Estoy más preocupado accidentalmente . Lo que significa es que este uso de this es parte de la interfaz expuesta de su clase, y debe documentarse. A veces se desea la capacidad de otro código para utilizar su bloqueo. Esto es cierto para cosas como Collections.synchronizedMap (vea el javadoc).

  2. Todos los métodos sincronizados dentro de la misma clase usan el mismo bloqueo exacto, lo que reduce el rendimiento

    Este es un pensamiento demasiado simplista; simplemente deshacerse de synchronized(this) no resolverá el problema. Sincronización adecuada para el rendimiento tomará más reflexión.

  3. Está (innecesariamente) exponiendo demasiada información

    Esta es una variante de # 1. El uso de synchronized(this) es parte de su interfaz. Si no quieres / necesitas que esto quede expuesto, no lo hagas.


Parece que hay un consenso diferente en los campos de C # y Java sobre esto. La mayoría del código de Java que he visto utiliza:

// apply mutex to this instance synchronized(this) { // do work here }

mientras que la mayoría del código C # opta por el más seguro:

// instance level lock object private readonly object _syncObj = new object(); ... // apply mutex to private instance level field (a System.Object usually) lock(_syncObj) { // do work here }

El lenguaje C # es ciertamente más seguro. Como se mencionó anteriormente, no se puede hacer un acceso malicioso / accidental al bloqueo desde fuera de la instancia. El código de Java también tiene este riesgo, pero parece que la comunidad de Java ha gravitado a lo largo del tiempo hacia la versión ligeramente menos segura, pero ligeramente más concisa.

No está pensado como una excavación contra Java, solo un reflejo de mi experiencia trabajando en ambos idiomas.


Respuesta corta : hay que entender la diferencia y elegir según el código.

Respuesta larga : en general, preferiría evitar la sincronización (esto) para reducir la contención, pero los bloqueos privados agregan complejidad de la que debe estar consciente. Entonces use la sincronización correcta para el trabajo correcto. Si no tiene tanta experiencia con la programación de subprocesos múltiples, prefiero seguir con el bloqueo de instancias y leer sobre este tema. (Dicho esto: el uso de sincronizar (esto) no hace que su clase sea completamente segura para subprocesos). Este no es un tema fácil, pero una vez que se acostumbra, la respuesta de usar sincronización (esto) o no es algo natural. .


Un buen ejemplo de uso sincronizado (este).

// add listener public final synchronized void addListener(IListener l) {listeners.add(l);} // remove listener public final synchronized void removeListener(IListener l) {listeners.remove(l);} // routine that raise events public void run() { // some code here... Set ls; synchronized(this) { ls = listeners.clone(); } for (IListener l : ls) { l.processEvent(event); } // some code here... }

Como puede ver aquí, usamos la sincronización en esto para facilitar la cooperación prolongada (posiblemente un bucle infinito del método de ejecución) con algunos métodos sincronizados allí.

Por supuesto, se puede reescribir muy fácilmente utilizando el campo sincronizado en privado. Pero a veces, cuando ya tenemos algún diseño con métodos sincronizados (es decir, clase heredada, derivamos de, sincronizado (esta) puede ser la única solución).


Creo que los puntos uno (alguien más que usa su candado) y dos (todos los métodos que usan el mismo candado innecesariamente) pueden suceder en cualquier aplicación bastante grande. Especialmente cuando no hay buena comunicación entre desarrolladores.

No está fundido en piedra, es principalmente una cuestión de buenas prácticas y prevención de errores.


Depende de la tarea que quieras hacer, pero no la usaría. Además, ¿compruebe si el proceso de guardado del hilo que desea acompañar no se puede realizar sincronizando (esto) en primer lugar? También hay algunos bloqueos agradables en la API que podrían ayudarte :)


Esto es realmente complementario a las otras respuestas, pero si su principal objeción al uso de objetos privados para el bloqueo es que llena su clase con campos que no están relacionados con la lógica empresarial, entonces Project Lombok tiene @Synchronizedque generar la repetición en tiempo de compilación:

@Synchronized public int foo() { return 0; }

compila a

private final Object $lock = new Object[0]; public int foo() { synchronized($lock) { return 0; } }