2010-03-11 16 views
9

Tengo una aplicación que cada 15 minutos más o menos realiza una replicación desde una base de datos remota. Simplemente mantiene los dos repositorios sincronizados. Una vez que se lleva a cabo esta replicación, no es posible volver a hacerlo. He configurado la siguiente estructura, pero no estoy seguro de si es el enfoque correcto.Cómo bloquear un método java para proteger múltiples invocaciones

public class ReplicatorRunner { 

     private static Lock lock = new ReentrantLock(); 

     public replicate() { 

      if (lock.tryLock()) { 
       try { 
        // long running process 
       } catch (Exception e) {     
       } finally { 
        lock.unlock(); 
       }    
      } else { 
       throw new IllegalStateException("already replicating"); 
      } 

     } 

} 

public class ReplicatorRunnerInvocator { 

    public void someMethod() { 

     try { 
      ReplicatorRunner replicator = new ReplicatorRunner(); 
      replicator.replicate(); 
     } catch (IllegalStateException e) { 
      e.printStackTrace(); 
     } 

    } 

} 

El ReplicatorRunner es la clase propietaria del método replicate que sólo se puede ejecutar uno a la vez.

Editar. Necesito que la siguiente llamada falle (no bloque) si el método ya se está ejecutando en cualquier instancia.

+0

¿Hay una pregunta aquí en alguna parte? – shoosh

+1

Yeap, en el título ... amigo, no tengo tiempo para ayudarte totalmente, pero verifica la palabra clave sincronizada ... –

+0

La sincronizada está en una instancia por instancia o por clase (si es estática) y bloques. Quiero que falle y no bloquear. – rmarimon

Respuesta

4

Esto se ve bien. ReentrantLock.tryLock() solo dará el bloqueo a un hilo, por lo que synchronized no es necesario. También evita el bloqueo inherente a la sincronización que usted dice que es un requisito. ReentrantLock es Serializable, por lo que debería funcionar en su clúster.

Hazlo.

+0

Voy con esto, pero tengo que verificar que la entrada en el bloque bloqueado con la siguiente línea para evitar llamadas múltiples de la misma cadena: 'if (lock.tryLock() && getHoldCount() == 1) ' – rmarimon

+0

En realidad ... tuve que cambiar esto una vez más. Como estaba lanzando un hilo para hacer el cálculo largo, no pude desbloquear el bloqueo de ese otro hilo. Cambié la lógica a usar un semáforo con 1 permiso. Pondré la lógica como otra respuesta en aras de la integridad. – rmarimon

0

echar un vistazo a la clase de semáforo here o de la marca como el método sincronizado el hilo de la ejecución del método en un momento dado es propietaria de un bloqueo en ella evitando otros hilos para llamar al método hasta que termine su ejecución.
Editar: si desea que los otros hilos fallen, puede usar un Lock, y probar si el bloqueo está disponible con el método tryLock.

1

Cambio public replicate() a public synchronized replicate()

De esa manera réplica referida solamente permitir el acceso a un hilo a la vez. También podrá eliminar ReentrantLock y todo el código asociado.

+0

Solo puede haber un método de réplica ejecutándose en un momento dado porque estropeará la replicación si dos o más lo hacen. La sincronización se bloqueará por instancia. Podría sincronizar el bloque en una clase estática pero eso bloqueará. Necesito que la ejecución falle si el método ya se está ejecutando en cualquier instancia. – rmarimon

0

Sin tener en cuenta los detalles de ReentrantLock, se me ocurre que esta prevención de múltiples rutinas de replicación simultáneas estará limitada a una única instancia de JVM.

Si se inicia otra instancia de la clase en una JVM por separado, entonces es posible que tenga problemas.

¿Por qué no poner un mecanismo de bloqueo en la base de datos? es decir, una fila en una tabla de control que se establece en un valor que representa si la replicación está ocupada o no, y restablece el valor cuando finaliza la replicación.

+0

Es cierto. Las características simultáneas del paquete son bastante impresionantes, pero no manejarán la sincronización multi-JVM. Controlarlo a nivel de base de datos puede ser su mejor opción aquí. O bien, necesitará una forma para que sus instancias se comuniquen entre sí (RMI u otro), pero eso podría complicarse. – Etienne

+0

La replicación se produce en la memoria de la JVM, por lo que no hay problema. Y podemos tener múltiples JVMs replicando en la base de datos. El problema no es que la base de datos sea el método múltiple que se ejecuta inadvertidamente dentro de la JVM. – rmarimon

1

Terminé usando los siguientes:

public class ReplicatorRunner { 

     private static Semaphore lock = new Semaphore(1); 

     public replicate() { 

      if (lock.tryAcquire()) { 
       try {        
        // basic setup     
        Thread t = new Thread(new Runnable() { 
         public void run() { 
          try { 
           // long running process        
          } catch Exception (e) { 
           // handle the exceptions 
          } finally { 
           lock.release(); 
          } 
         } 
        }) 
        t.start(); 

       } catch (Exception e) { 
        // in case something goes wrong 
        // before the thread starts 
        lock.release(); 
       }    
      } else { 
       throw new IllegalStateException("already replicating"); 
      } 

     } 

} 

public class ReplicatorRunnerInvocator { 

    public void someMethod() { 

     try { 
      ReplicatorRunner replicator = new ReplicatorRunner(); 
      replicator.replicate(); 
     } catch (IllegalStateException e) { 
      e.printStackTrace(); 
     } 

    } 

} 
Cuestiones relacionadas