2010-03-11 10 views
9

Ho un'applicazione che ogni 15 minuti circa esegue una replica da un database remoto. Mantiene solo i due repository sincronizzati. Una volta che questa replica sta andando, non è possibile farlo di nuovo. Ho configurato la seguente struttura, ma non sono sicuro che sia l'approccio corretto.Come bloccare un metodo java per proteggere più chiamate

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(); 
     } 

    } 

} 

Il ReplicatorRunner è la classe che possiede il metodo replicate che può essere eseguito solo uno alla volta.

Modifica. Ho bisogno della prossima chiamata per fallire (non bloccare) se il metodo è già in esecuzione su qualsiasi istanza.

+0

C'è una domanda qui da qualche parte? – shoosh

+1

Yeap, nel titolo ... amico, non ho il tempo di aiutarti completamente ma controlla la parola chiave sincronizzata ... –

+0

La sincronizzazione è su una istanza o su una classe (se è statica) e blocchi. Voglio che fallisca e non blocchi. – rmarimon

risposta

4

Questo sembra buono. ReentrantLock.tryLock() assegnerà il blocco solo a un thread, quindi synchronized non è necessario. Inoltre impedisce il blocco inerente alla sincronizzazione che si dice sia un requisito. ReentrantLock è serializzabile, quindi dovrebbe funzionare in tutto il cluster.

Vai per questo.

+0

Sto andando con questo, ma devo controllare che la voce nel blocco bloccato con la seguente riga in modo da evitare più chiamate dallo stesso thread: 'if (lock.tryLock() && getHoldCount() == 1) ' – rmarimon

+0

In realtà ... dovevo cambiarlo ancora una volta. Dal momento che stavo lanciando una discussione per fare il calcolo lungo non riuscivo a sbloccare il blocco da quell'altro thread. Ho cambiato la logica di usare un semaforo con 1 permesso. Metterò la logica come un'altra risposta per ragioni di completezza. – rmarimon

0

un'occhiata alla classe semaforo here o contrassegnare il metodo sincronizzato il filo esecuzione del metodo in un dato momento possiede una serratura evitando altri thread per chiamare il metodo fino alla sua esecuzione termina.
Modifica: se si desidera che gli altri thread non riescano, è possibile utilizzare uno Lock e verificare se il blocco è disponibile con il metodo tryLock.

1

Change public replicate()-public synchronized replicate()

In questo modo replica sarà sempre e solo consentirà l'accesso a un thread alla volta. Sarai anche in grado di eliminare ReentrantLock e tutto il codice associato.

+0

Esiste solo un metodo di replica in esecuzione in un dato momento poiché comprometterà la replica se due o più lo fanno. La sincronizzazione verrà bloccata per ogni istanza. Potrei sincronizzare il blocco su una classe statica ma questo bloccherà. Ho bisogno che l'esecuzione fallisca se il metodo è già in esecuzione su qualsiasi istanza. – rmarimon

0

Senza guardare le specifiche del ReentrantLock, mi viene in mente che questa prevenzione di più routine di replica simultanea sarà limitata a una singola istanza JVM.

Se un'altra istanza della classe viene avviata in una JVM separata, potrebbe essere nei guai.

Perché non inserire un meccanismo di blocco nel database? Ad esempio, una riga in una tabella di controllo impostata su un valore che indica se la replica è occupata o meno, e reimpostare il valore al termine della replica.

+0

Vero. Le funzionalità del pacchetto concorrente sono piuttosto impressionanti, ma non gestiranno la sincronizzazione multi-JVM. Controllarlo a livello di database potrebbe essere la soluzione migliore qui. O avrai bisogno di un modo in cui le tue istanze possano comunicare tra loro (RMI o altro), ma ciò potrebbe complicare. – Etienne

+0

La replica si verifica nella memoria della JVM quindi nessun problema lì. E possiamo avere più JVM che si replicano nel DB. Il problema non è il DB è i molteplici metodi inavvertitamente in esecuzione all'interno della JVM. – rmarimon

1

ho finito per usare le seguenti:

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(); 
     } 

    } 

} 
Problemi correlati