tipos sincronizacion resueltos programas multihilos hilos fuente entre ejemplos con comunicacion codigo java multithreading synchronization

java - sincronizacion - Sincronización en las variables locales.



sincronizacion de hilos en java (7)

Tengo un código Java multiproceso en el que:

  • varios subprocesos leen objetos con estado desde el almacenamiento compartido sincronizado (es decir, como resultado de esto, algunos subprocesos pueden hacer referencia a los mismos objetos);
  • cada hilo invoca entonces un método process() y pasa su objeto allí;
  • process() procesa los objetos de alguna manera, lo que puede resultar en cambiar el estado de los objetos;
  • Estos cambios de estado deben estar sincronizados.

He creado un método como ese:

public void process(Foo[] foos) { for (final Foo foo : foos) { if (foo.needsProcessing()) { synchronized (foo) { foo.process(); // foo''s state may be changed here } } } }

Que yo sepa, esto parece legítimo. Sin embargo, la inspección de IntelliJ se queja de la sincronización en la variable local porque "es muy probable que diferentes subprocesos tengan diferentes instancias locales" (esto no es válido para mí, ya que no estoy inicializando foos en el método).

Esencialmente, lo que quiero lograr aquí es lo mismo que tener el método Foo.process () sincronizado (lo cual no es una opción para mí, ya que Foo es parte de una biblioteca de terceros).

Me acostumbré al código sin marcas amarillas, por lo que se agradece cualquier consejo de la comunidad. ¿Es realmente tan malo hacer la sincronización en los locales? ¿Hay alguna alternativa que funcione en mi situación?

¡Gracias por adelantado!


En el mundo .NET, los objetos a veces llevan su objeto de bloqueo como propiedad.

synchronized (foo.getSyncRoot()) { if (foo.needsProcessing()) { foo.process(); // foo''s state may be changed here } }

Esto permite que el objeto proporcione un objeto de bloqueo diferente, dependiendo de su implementación (por ejemplo, delegar el monitor a una conexión de base de datos subyacente o algo así).


La respuesta de Stephen C tiene problemas, está ingresando a una gran cantidad de bloqueos de sincronización inútilmente, curiosamente, la mejor forma de formatearlo es:

public void process(Foo[] foos) { for (final Foo foo : foos) { if (foo.needsProcessing()) { synchronized (foo) { if (foo.needsProcessing()) { foo.process(); // foo''s state may be changed here } } } } }

Obtener el bloqueo de sincronización a veces puede llevar un tiempo y, si se mantiene, algo está cambiando algo. Podría ser algo cambiado el estado de necesidad de procesamiento de foo en ese momento.

No desea esperar el bloqueo si no necesita procesar el objeto. Y después de obtener el bloqueo, es posible que aún no necesite procesamiento. Entonces, aunque parezca un poco tonto y los programadores novatos podrían estar inclinados a eliminar uno de los controles, en realidad es la mejor manera de hacerlo siempre que foo.needsProcessing () sea una función despreciable.

Devolviendo esto a la pregunta principal, cuando se trata de una sincronización basada en los valores locales de una matriz, es porque el tiempo es crítico. Y en esos casos, lo último que desea hacer es bloquear todos los elementos de la matriz o procesar los datos dos veces. Solo estarías sincronizando objetos locales si tienes un par de subprocesos que hacen un montón de trabajo y rara vez deberías tocar los mismos datos, pero muy bien podría.

Hacer esto solo afectará el bloqueo si, y solo si, el procesamiento de ese foo que necesita procesamiento causaría errores de concurrencia. Básicamente, querrá una sintaxis de doble puerta en aquellos casos en los que desee sincronizar basándose solo en los objetos precisos de la matriz como tales. Esto evita el doble procesamiento de foos y el bloqueo de cualquier foo que no necesite procesamiento.

Se queda con el caso muy raro de bloquear el hilo o incluso de ingresar un bloqueo solo y exactamente cuando es importante, y su hilo solo está bloqueado en el punto exacto en el que el no bloqueo podría dar lugar a errores de concurrencia.


Ninguna inteligencia automática es perfecta. Creo que tu idea de sincronizar en la variable local es bastante correcta. Así que quizás hágalo a su manera (lo cual es correcto) y sugiera a JetBrains que deberían ajustar su inspección.


No hay nada malo con su sincronización en un objeto dentro de una colección. Puede intentar reemplazar el foreach con un bucle for normal:

for (int n = 0; n < Foos.length; n++) { Foo foo = Foos[n]; if (null != foo && foo.needsProcessing()) { synchronized (foo) { foo.process(); // foo''s state may be changed here } } }

o incluso (para que el detector no se tropiece con Foo foo ):

for (int n = 0; n < foos.length; n++) { if (null != foos[n] && foos[n].needsProcessing()) { synchronized (foos[n]) { foos[n].process(); // foos[n]''s state may be changed here } } }

No usar un valor temporal para evitar los múltiples foos[n] no es la mejor práctica, pero si funciona para evitar la advertencia no deseada, es posible que viva con él. Añadir un comentario por qué este código tiene una forma desviada. :-)


Refactorice el bucle-cuerpo en un método separado tomando foo como parámetro.


Se supone que IDE le ayudará, si comete un error, no debe inclinarse y complacerlo.

Puede deshabilitar esta inspección en IntelliJ. (Es curioso que sea el único habilitado de forma predeterminada en "Temas de subprocesos". ¿Es este el error más frecuente que cometen las personas?)


if (foo.needsProcessing()) { synchronized (foo) { foo.process(); // foo''s state may be changed here } }

Creo que hay una condición de carrera en el fragmento anterior que podría ocasionar que foo.process() sea ​​llamado ocasionalmente dos veces en el mismo objeto. Debería ser:

synchronized (foo) { if (foo.needsProcessing()) { foo.process(); // foo''s state may be changed here } }

¿Es realmente tan malo sincronizar en los locales?

No es malo sincronizar en los locales per se . Los problemas reales son:

  • si los diferentes subprocesos se sincronizan en los objetos correctos para lograr la sincronización correcta, y

  • si algo más podría causar problemas al sincronizar en esos objetos.