views:

280

answers:

5

I have an application that every 15 minutes or so does a replication from a remote database. It just keeps the two repositories in sync. Once this replication is going it is not possible to do it again. I have setup the following structure but I'm not sure if it is the correct approach.

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

    }

}

The ReplicatorRunner is the class owning the method replicate which can only be run one at a time.

Edit. I need the next call to fail (not block) if the method is already running on any instance.

A: 

take a look at the Semaphore class here or mark the method as synchronized the thread executing the method at any given time owns a lock on it avoiding other threads to call the method until its execution ends.
Edit: if you want the other threads to fail, you could use a Lock, and test if the lock is avaible by the tryLock method.

marcos
A: 

Change public replicate() to public synchronized replicate()

That way replicate will only ever allow access to one thread at a time. You'll also be able to delete the ReentrantLock and all associated code.

Scobal
There can only be one replicate method running at any given time because it will mess up the replication if two or more do it. The synchronize will block on a per instance basis. I could synchronize the block on a static class but that will block. I need the execution to fail if the method is already running on any instance.
rmarimon
A: 

Without looking at the specifics of the ReentrantLock, it occurs to me that this prevention of multiple simultaneous replication routines will be limited to a single JVM instance.

If another instance of the class is kicked off in a separate JVM, then you might be in trouble.

Why not put a lock mechanism on the database? i.e. A row in a control table that is set to a value depicting whether or not the replication is busy running, and reset the value when the replication is finished.

crowne
True. Concurrent package features are quite awesome, but they won't handle multi-JVM synchronization.Controlling it at the database level might be your better option here. Or you will need a way for your instances to communicate with each other (RMI or else) but that might get complicated.
Etienne
The replication occurs to memory of the JVM so no problem there. And we can have multiple JVMs replicating to the DB. The problem is not the DB is the multiple methods inadvertently running inside the JVM.
rmarimon
+2  A: 

This looks good. ReentrantLock.tryLock() will only give the lock to one thread, so synchronized is not necessary. It also prevents the blocking inherent in synchronization that you say is a requirement. ReentrantLock is Serializable, so should work across your cluster.

Go for it.

Matthew Flynn
rmarimon
Actually... i had to change this one more time. Since I was launching a thread to do the long calculation I couldn't unlock the lock from that other thread. I changed the logic to using a semaphore with 1 permit. I will put the logic as another answer for the sakes of completeness.
rmarimon
A: 

I ended up using the following:

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

    }

}
rmarimon