2011-03-09 13 views
5

Digamos que tengo un método, al que se accede por dos o más subprocesos y quiero que sea seguro para subprocesos.Sincronización de los parámetros formales en java

public int getVal(int x, int y, MyClass myObj) 
{ 
    int z; 

    z = getInt(myObj); 

    return x + y + z; 
} 

En esto, creo que no tenemos que sincronizar para x + y ya que son primitivos.

Supongamos que getInt(myObj) modifica el estado de myObj y que influye en el valor de z.

Por lo tanto, tendré que proporcionar la sincronización para la línea z = getInt(myObj); pero solo cuando ambos hilos pasen la misma instancia en la referencia 'myObj'. Como el codificador de la API no sabría si ambos hilos pasarían la misma instancia para 'myObj' o no. En algunos casos, estos hilos pueden pasar la misma instancia de MyClass en la referencia 'myObj' y en otros casos pueden pasar diferentes instancias de MyClass en la referencia 'myObj'.

Entonces, ¿cómo se puede garantizar la seguridad del hilo para la línea z = getInt(myObj)? (Seguramente, no queremos sincronizar cuando las instancias pasadas son diferentes y solo tienen que sincronizarse cuando las instancias pasadas son iguales. Es claro que esto no se puede determinar).

Suponiendo que MyClass no se puede convertir en inmutable, creo que lo siguiente podría ser una solución.

synchronized(myObj) 
{ 
    z = getInt(myObj); 
} 

¿Es la solución correcta? Y, en lo que otras maneras podemos garantizar la seguridad de los subprocesos para

z = getInt(myObj); (but only in case of different instances)? 
+0

su bloque sincronizado es suficiente. La JVM bloquea en esa llamada cada vez que el monitor para el objeto sincronizado es retenido por otra cadena. –

+0

@Brent, ¿hay alguna otra manera de hacerlo? ¡Sólo me preguntaba! –

+0

Su sincronizado() es peligroso. revisa mi respuesta a continuación. – iluxa

Respuesta

0

Para contestar "y, en lo que son las formas podemos garantizar la seguridad de rosca para ... pero sólo en caso de diferentes instancias", sincronizar la totalidad método o crear otro objeto común para actuar como un bloqueo para todos los hilos y sincronizar en él en lugar de myObj.

3

Lo que tiene es correcto. Cuando synchronize en un objeto está bloqueando en esa instancia no en esa clase. Entonces, si paso la misma instancia * de un objeto a dos métodos diferentes, se bloqueará en ese objeto correctamente. Sin embargo, si paso dos instancias diferentes, no habrá ningún bloqueo porque dos instancias tienen cada uno su propia cerradura .

+3

Una mejor manera es sincronizar el método getInt en su MyClass. –

+0

Lo que tiene es correcto, pero es una mala práctica. Al bloquear myObj podría estar introduciendo riesgos al código del cliente que también quiere sincronizarse en ese objeto. http://stackoverflow.com/questions/442564/avoid-synchronizedthis-in-java/442601#442601 –

+0

Es cierto, pero si delega el trabajo en getInt() y utiliza un objeto interno para hacer el bloqueo, creo que será conjunto. Pero sí, estoy de acuerdo, esto tiene una trampa. –

2

Si getInt no modifica el estado de this, entonces el método es seguro para subprocesos. La seguridad de subprocesos del objeto myObj es la responsabilidad de su clase: MyClass, o del objeto que lo contiene. No es responsabilidad de todos los métodos que podrían tomarlo como argumento, en mi humilde opinión.

Su solución (synchronized(myObj)) es correcta, sin embargo: dos hilos no podrán ejecutar el método getInt al mismo tiempo si se usa el mismo myObj en ambos hilos. Se ejecutarán concurrentemente si los dos myObjs son diferentes.

+0

¿Qué pasa si alguien está llamando simultáneamente a 'setInt'? Aún puede tener problemas para leer si alguien más puede estar escribiendo. –

+0

No es un getter (acceso), supongo que extrae o deriva algo de la entrada, por lo que no es probable que exista un setInt. – Robin

+0

"La seguridad de subprocesos del objeto myObj es responsabilidad de su clase": ¿Es común que las bibliotecas Java sean inherentemente seguras para subprocesos? O, al igual que la biblioteca de colecciones, ¿se supone que el usuario agregará el bloqueo cuando corresponda? – Karmastan

0

Si los únicos cambios en myObject en cuestión provienen de este método getInt, entonces la sincronización es suficiente. Si hay otros modificadores, asegúrese de que se sincronicen en el mismo objeto.

0

No estoy de acuerdo con todas las respuestas "su sincronizado es correcto". ¿Qué sucede si el usuario tiene 2 hilos y uno de ellos ya tiene bloqueado el objeto? Se producen bloqueos.

Además, x + y + z no es atómico. A nivel de la CPU, que va a convertirse en

int temp = x + y; 
int res = temp + z; 

Te diré más: LONG1 + long2 no es atómica en máquinas de 32 bits.

Creo que su única opción es sincronizar todo el método.

+1

la adición de primitivas en su método está bien ya que las primitivas son pasadas por valor. Los parámetros formales primitivos de un método no se pueden compartir entre hilos, ya que pasan de un valor a otro. –

1
synchronized(myObj) { z = getInt(myObj); } 

hará lo que usted desea, pero la sincronización en un parámetro crea muchos otros problemas. Por ejemplo, es posible que algún otro subproceso ya se esté sincronizando en ese objeto (p. Ej., Tal vez ese objeto tenga un método sincronizado al que se está llamando) y podría entrar en un caso de interbloqueo.

La sincronización debe ser encapsulada como cualquier otra cosa. La mejor solución sería agregar el método getInt al MyClass y sincronizar en algún miembro privado dentro de ese método. De esta forma, nadie más puede ensuciar con lo que está utilizando para implementar su sincronización.

ej .:

public class MyClass { 
    private Object lockObject = new Object(); 
    public int getInt() { 
    synchronized(lockObject) { 
     // do your thing 
    } 
    } 
} 

ver esto: Avoid synchronized(this) in Java? en referencia a la importancia de encapsular su sincronización.

Cuestiones relacionadas