uso titledborder poner definicion con borde java multithreading concurrency synchronization java.util.concurrent

java - titledborder - Sincronización vs bloqueo



titledborder java (9)

java.util.concurrent API proporciona una clase llamada Lock , que básicamente serializaría el control para acceder al recurso crítico. unpark() método como park() y unpark() .

Podemos hacer cosas similares si podemos usar synchronized palabra clave synchronized y usar los métodos wait() y notify() notifyAll() .

Me pregunto cuál de estos es mejor en la práctica y por qué?


Me pregunto cuál de estos es mejor en la práctica y por qué?

Descubrí que Lock and Condition (y otras clases concurrent nuevas) son solo más herramientas para la caja de herramientas. Podía hacer casi todo lo que necesitaba con mi viejo martillo de garra (la palabra clave synchronized ), pero era incómodo de usar en algunas situaciones. Varias de esas situaciones incómodas se volvieron mucho más simples una vez que agregué más herramientas a mi caja de herramientas: un mazo de goma, un martillo de bolas, una palanca y algunos golpes de uñas. Sin embargo , mi viejo martillo de garra todavía ve su uso compartido.

No creo que uno sea realmente "mejor" que el otro, sino que cada uno se adapta mejor a diferentes problemas. En pocas palabras, el modelo simple y la naturaleza orientada al alcance de synchronized me ayudan a protegerme de errores en mi código, pero esas mismas ventajas a veces son obstáculos en escenarios más complejos. Es estos escenarios más complejos que el paquete simultáneo se creó para ayudar a abordar. Pero el uso de estos constructos de mayor nivel requiere una administración más explícita y cuidadosa en el código.

===

Creo que el JavaDoc hace un buen trabajo al describir la distinción entre Lock y synchronized (el énfasis es mío):

Las implementaciones de bloqueo proporcionan operaciones de bloqueo más extensas que las que se pueden obtener utilizando métodos y declaraciones sincronizados. Permiten una estructuración más flexible , pueden tener propiedades bastante diferentes y pueden admitir múltiples objetos Condition asociados .

...

El uso de métodos o declaraciones sincronizadas proporciona acceso al bloqueo de monitor implícito asociado con cada objeto, pero obliga a que todas las bloqueos de adquisición y liberación se produzcan de forma bloqueada : cuando se adquieren múltiples bloqueos , deben liberarse en el orden opuesto , y todos los bloqueos deben ser liberados en el mismo alcance léxico en el que fueron adquiridos .

Si bien el mecanismo de alcance para métodos y declaraciones sincronizados hace que sea mucho más fácil programar con bloqueos de monitor y ayuda a evitar muchos errores de programación comunes relacionados con bloqueos, hay ocasiones en las que necesita trabajar con bloqueos de una manera más flexible. Por ejemplo, * * algunos algoritmos * para atravesar estructuras de datos accedidas simultáneamente requieren el uso de "mano sobre mano" o "bloqueo de cadena" : usted adquiere el bloqueo del nodo A, luego el nodo B, luego libera A y adquiere C, luego suelta B y adquieres D, y así sucesivamente. Las implementaciones de la interfaz de bloqueo permiten el uso de tales técnicas al permitir que un bloqueo sea adquirido y liberado en diferentes ámbitos , y permitiendo que se adquieran y liberen múltiples bloqueos en cualquier orden .

Con esta mayor flexibilidad viene una responsabilidad adicional . La ausencia de bloqueo estructurado elimina la liberación automática de bloqueos que se produce con métodos y declaraciones sincronizados. En la mayoría de los casos, se debe usar el siguiente modismo:

...

Cuando se bloquea y desbloquea en diferentes ámbitos , se debe tener cuidado para garantizar que todo el código que se ejecuta mientras se mantiene el bloqueo esté protegido por try-finally o try-catch para garantizar que se libere el bloqueo cuando sea necesario.

Las implementaciones de bloqueo proporcionan una funcionalidad adicional sobre el uso de métodos y declaraciones sincronizados al proporcionar un intento de no bloqueo para adquirir un bloqueo (tryLock ()), un intento de adquirir el bloqueo que se puede interrumpir (lockInterruptibly (), y un intento de adquirir el bloqueo que puede agotar el tiempo de espera (tryLock (long, TimeUnit)).

...


El libro de Brian Goetz "Java Concurrency in Practice", sección 13.3: "... Al igual que el ReentrantLock predeterminado, el bloqueo intrínseco no ofrece garantías deterministas de equidad, pero las garantías estadísticas de equidad de la mayoría de las implementaciones de bloqueo son lo suficientemente buenas para casi todas las situaciones ..."


Hay 4 factores principales en por qué desearía usar synchronized o java.util.concurrent.Lock .

Nota: El bloqueo sincronizado es lo que quiero decir cuando digo bloqueo intrínseco.

  1. Cuando Java 5 salió con ReentrantLocks, probaron tener una diferencia de rendimiento bastante notable y luego un bloqueo intrínseco. Si estás buscando un mecanismo de bloqueo más rápido y estás ejecutando 1.5, considera jucReentrantLock. El bloqueo intrínseco de Java 6 ahora es comparable.

  2. jucLock tiene diferentes mecanismos para bloquear. Bloqueo interrumpible: intente bloquear hasta que se interrumpa el hilo de bloqueo; bloqueo temporizado: intente bloquear durante un cierto período de tiempo y renuncie si no tiene éxito; tryLock - intento de bloqueo, si algún otro hilo mantiene el bloqueo abandonado. Todo esto se incluye aparte del bloqueo simple. El bloqueo intrínseco solo ofrece un bloqueo simple

  3. Estilo. Si tanto 1 como 2 no entran dentro de las categorías de lo que le preocupa, la mayoría de las personas, incluyéndome a mí, encontrarán que los semenáticos de bloqueo intrínsecos son más fáciles de leer y menos detallados que el bloqueo de juclock.
  4. Múltiples condiciones. Un objeto que encierre solo se puede notificar y esperar por un solo caso. El nuevo método de Condición de Lock permite que un único bloqueo tenga múltiples motivos para esperar o señalizar. Todavía tengo que necesitar esta funcionalidad en la práctica, pero es una buena característica para quienes la necesitan.

La principal diferencia entre bloqueo y sincronización es que, con bloqueos, puede liberar y adquirir los bloqueos en cualquier orden. - con sincronización, puede liberar los bloqueos solo en el orden en que fue adquirido.


La principal diferencia es la equidad, en otras palabras, ¿se manejan las solicitudes FIFO o puede haber intrusión? La sincronización a nivel de método asegura una asignación justa o FIFO del bloqueo. Utilizando

synchronized(foo) { }

o

lock.acquire(); .....lock.release();

no asegura la equidad

Si tiene muchas disputas por el bloqueo, puede encontrar fácilmente un estallido en el que las solicitudes más recientes obtienen el bloqueo y las solicitudes anteriores se atascan. He visto casos en los que llegan 200 hilos en poco tiempo para un candado y el segundo en llegar fue procesado el último. Esto está bien para algunas aplicaciones pero para otras es mortal.

Consulte la sección 13.3 del libro "Concurrencia en práctica" de Brian Goetz para una discusión completa de este tema.


Lock hace la vida de los programadores más fácil. Aquí hay algunas situaciones que se pueden lograr más fácilmente con el bloqueo.

  1. Bloquee en un método y libere el bloqueo en otro método.
  2. Tiene dos hilos trabajando en dos partes de código diferentes, sin embargo, en el primer hilo hay dependencia en el segundo hilo para completar un fragmento de código antes de continuar (mientras que otros hilos también funcionan simultáneamente). Un bloqueo compartido puede resolver este problema con bastante facilidad.
  3. Implementando monitores. Por ejemplo, una cola simple donde los métodos de poner y obtener se ejecutan desde muchos hilos diferentes. Sin embargo, ¿desea sobrepasar los mismos métodos uno sobre otro, ni los métodos de poner y obtener pueden superponerse? En tal caso, un candado privado hace la vida mucho más fácil.

Mientras tanto, el bloqueo y las condiciones se basan en el sincronizado. Por lo tanto, puede lograr el mismo objetivo con eso. Sin embargo, eso puede dificultarle la vida y puede desviarlo de la solución del problema real.


Me gustaría agregar algunas cosas más además de la respuesta de Bert F.

Locks admiten varios métodos para un control de bloqueo más fino, que son más expresivos que los monitores implícitos (bloqueos synchronized )

Un bloqueo proporciona acceso exclusivo a un recurso compartido: solo un hilo a la vez puede adquirir el bloqueo y todo acceso al recurso compartido requiere que el bloqueo se adquiera primero. Sin embargo, algunos bloqueos pueden permitir el acceso simultáneo a un recurso compartido, como el bloqueo de lectura de un ReadWriteLock.

Ventajas de Lock over Synchronization desde la Lock documentación

  1. El uso de métodos o declaraciones sincronizadas proporciona acceso al bloqueo de monitor implícito asociado a cada objeto, pero obliga a que todas las capturas y liberaciones de bloqueos se realicen de forma bloqueada

  2. Las implementaciones de bloqueo proporcionan una funcionalidad adicional sobre el uso de métodos y declaraciones sincronizados al proporcionar un intento de no bloqueo para adquirir un lock (tryLock()) , un intento de adquirir el bloqueo que se puede interrumpir ( lockInterruptibly() , y un intento de adquirir el bloqueo que puede timeout (tryLock(long, TimeUnit)) .

  3. Una clase de bloqueo también puede proporcionar un comportamiento y semántica que es bastante diferente de la del bloqueo de monitor implícito, como el orden garantizado, el uso sin reentrada o la detección de punto muerto.

ReentrantLock : en términos simples, según mi comprensión, ReentrantLock permite que un objeto vuelva a ingresar de una sección crítica a otra sección crítica. Como ya tiene bloqueo para ingresar a una sección crítica, puede hacer otra sección crítica en el mismo objeto utilizando el bloqueo actual.

ReentrantLock características clave según este article

  1. Posibilidad de bloquear de forma interrumpible.
  2. Capacidad de tiempo de espera mientras espera el bloqueo.
  3. Poder para crear bloqueo justo.
  4. API para obtener la lista de hilo en espera para el bloqueo.
  5. Flexibilidad para intentar bloquear sin bloquear.

Puede usar ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock para adquirir más control sobre el bloqueo granular en las operaciones de lectura y escritura.

Además de estos tres ReentrantLocks, java 8 proporciona un bloqueo más

StampedLock:

Java 8 se envía con un nuevo tipo de bloqueo llamado StampedLock que también admite bloqueos de lectura y escritura como en el ejemplo anterior. A diferencia de ReadWriteLock, los métodos de bloqueo de un StampedLock devuelven un sello representado por un valor largo.

Puede usar estos sellos para liberar un bloqueo o para verificar si el bloqueo sigue siendo válido. Además, los bloqueos estampados admiten otro modo de bloqueo denominado bloqueo optimista.

Eche un vistazo a este article sobre el uso de diferentes tipos de ReentrantLock y StampedLock .


Puede lograr todo lo que las utilidades en java.util.concurrent hacen con las primitivas de bajo nivel como synchronized , volatile o wait / notify

Sin embargo, la concurrencia es complicada, y la mayoría de las personas obtienen al menos algunas partes incorrectas, haciendo que su código sea incorrecto o ineficiente (o ambos).

La API concurrente proporciona un enfoque de nivel superior, que es más fácil de usar (y, por lo tanto, más seguro). En pocas palabras, no debería necesitar usar synchronized, volatile, wait, notify directamente nunca más.

La clase de Lock sí está en el lado inferior de esta caja de herramientas, es posible que ni siquiera necesite usarla directamente (puede usar Queues y Semaphore y demás, etc., la mayoría de las veces).


Si simplemente estás bloqueando un objeto, preferiría usar synchronized

Ejemplo:

Lock.acquire(); doSomethingNifty(); // Throws a NPE! Lock.release(); // Oh noes, we never release the lock!

Tienes que try{} finally{} explícitamente try{} finally{} todas partes.

Mientras que con sincronización, es súper claro e imposible equivocarse:

synchronized(myObject) { doSomethingNifty(); }

Dicho esto, Lock s puede ser más útil para cosas más complicadas donde no se puede adquirir y liberar de una manera tan limpia. Honestamente, preferiría evitar el uso de Lock s desnudo en primer lugar, y solo ir con un control de concurrencia más sofisticado, como un CyclicBarrier o un LinkedBlockingQueue , si satisfacen sus necesidades.

Nunca tuve una razón para usar wait() o notify() pero puede haber algunos buenos.