2010-06-28 14 views
6

Sé cuando quiere bloquear el método para que se ejecute con solo un hilo lo declara con la palabra clave synchronized.Cómo bloquear un método para una clase completa usando sincronizado?

¿Qué pasa con las clases, cómo proporcionar un bloqueo en una clase completa de objetos cuando un hilo está ejecutando algún código en una instancia de esa clase?

En otras palabras, cuando un hilo está ejecutando un método en un objeto, ningún otro hilo debe ser permitido ejecutar el mismo método incluso en una instancia diferente de la misma clase.

+1

¿Puede explicar por qué necesita hacer esto? Tengo la sensación de que podrías estar atacando el problema equivocado. Necesitar sincronizar llamadas de método en * instancias completamente diferentes de su clase * parece que el problema real está en otro lugar. –

Respuesta

8

sincronizar en un objeto específico, o bien un objeto de bloqueo estático designado, o la clase de objeto (lo que ocurre cuando se declaran los métodos estáticos para ser sincronizado):

class X { 
    private static final Object lock = new Object(); 
    public void oneAtATime() { 
     synchronized (lock) { 
      // Do stuff 
     } 
    } 
} 
class Y { 
    public void oneAtATime() { 
     synchronized (Y.class) { 
      // Do stuff 
     } 
    } 
} 

Cada variante tiene sus propias ventajas y desventajas ; el bloqueo de la clase permite que otro código, fuera de la clase, use el mismo bloqueo por sus propios motivos (lo que le permite orquestar más sincronización de alto nivel que la que proporciona) mientras que el método static final Object lock le permite prohibirlo haciendo el bloqueo campo privado (lo que hace que sea más fácil razonar sobre el bloqueo y evitar que su código se bloquee porque alguien más escribió código incorrecto).

Por supuesto podría también utilizar algún mecanismo de sincronización de java.util.concurrent, como explícitos Lock s, que proporcionan un mayor control de bloqueo (y ReentrantLock actualmente realiza un poco mejor que los bloqueos implícitos en condiciones de alta contención).


Editar: Tenga en cuenta que las cerraduras/globales estáticas no son una gran manera de ir - que significa que cada instancia de la clase jamás creado verán esencialmente ligada a todos los demás casos (que, aparte de que fuera más difícil de probar o leer el código, puede dañar gravemente la escalabilidad). Supongo que haces esto para sincronizar algún tipo de estado global. En ese caso, consideraría incluir ese estado global/estático en una clase e implementar la sincronización por instancia en lugar de implementarla globalmente.

En lugar de algo como esto:

class Z { 
    private static int state; 
    public void oneAtATime(){ 
     synchronized (Z.class) { 
      state++; 
     } 
    } 
} 

hacerlo de esta manera:

class State { 
    private int value; 
    public synchronized void mutate(){ value++; } 
} 
class Z { 
    private final State state; 
    public Z(State state){ 
     this.state = state; 
    } 
    public void oneAtATime(){ 
     state.mutate(); 
    } 
} 
// Usage: 
State s1 = new State(), s2 = new State(); 
Z foo = new Z(s1); 
Z bar = new Z(s1); 
Z frob = new Z(s2); 
Z quux = new Z(s2); 

Ahora foo y bar todavía están vinculados entre sí, pero pueden trabajar independientemente de frob y quux.

+0

Esta (última) manera es sin duda la mejor que diría. Hace que sea transparente lo que se supone que ocurre con las subclases de su clase, y con varias instancias de su programa, y ​​también de qué preocuparse cuando se necesitan métodos estáticos. –

2

Puede usar un Mutex estático dentro de ese método. Por lo tanto, cualquier subproceso simultáneo se bloquea dentro del método mientras que otro lo está ejecutando, sin importar a qué objeto pertenezca la clase. No creo que haya ninguna palabra clave especial para producir el mismo efecto, como synchronized.

Es una sincronización bastante agresiva, la evitaría tanto como sea posible.

0

No hay un mecanismo incorporado para esto. Cree su propio atributo de bloqueo estático y asegúrese de bloquearlo y desbloquearlo en todos los métodos. No te olvides de las excepciones: asegúrate de desbloquearlas en las secciones "finalmente".

5

Si usa métodos sincronizados estáticos, se bloquean a través del bloqueo de clase. También puede declarar un objeto estático en la clase y el bloqueo que en un método que yo creo a través de algo como:

private static final Object STATIC_LOCK = new Object(); 

private void foo() { 
    synchronized (STATIC_LOCK) { 
      //do stuff... 
    } 
} 
0

Esto debería funcionar:

public class MyClass { 
    void synchronizedMethod() { 
    synchronized (MyClass.class) { 
     // synchronized on static level 
    } 
    } 
} 

Qué 'missuses' en tiempo de ejecución representación de la clase de cierre. Esto es posible ya que cualquier objeto se puede usar como mutex en Java.

+0

Dado que los métodos sincronizados estáticos se sincronizan en el objeto de la clase, ¿por qué considera que es un uso incorrecto? –

+0

¿Es eso así? En este caso, si los diseñadores de Java lo hacen así, entonces probablemente no sea un uso indebido. Pensé que la forma más limpia era tener un objeto estático dedicado Monitor/Mutex, y que usar algún objeto era algo así como un truco, aunque yo personalmente optaría por esa opción yo mismo. Ponga a la señorita entre comillas ahora :) –

2

Sincronizar en campo estático de la clase, o de la propia clase:

synchronized(MyClass.class) { 
     // mutually excluded method body 
} 
1

Ambas roscas deben utilizar esta construcción

public void someMethod() { 
    synchronized(ClassThatShouldBeProtected.class) { 
    someSynchronizedCode(); 
    } 
} 

Este enfoque se beneficia del hecho, de que la clase en sí es un objeto y por lo tanto, tiene un monitor. Entonces no necesitas ninguna instancia estática artificial.

0

http://www.janeg.ca/scjp/threads/synchronization.html

habla de varias maneras para lograrlo. en general, las cerraduras son prohibitivas y dificultan los beneficios del enhebrado. por lo que el código crítico debe ser minimizado tanto como sea posible.

¿desea un bloqueo de palanca de clase para acceder a las variables estáticas de la clase o es para proteger el acceso a un recurso externo común de la clase? en cuyo caso deberías tener un bloqueo separado mientras accedes a él.

Cuestiones relacionadas