Ricardo Marimon
Ricardo Marimon

Reputation: 10697

How to lock a java method to protect multiple invocations

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.

Upvotes: 9

Views: 5841

Answers (5)

Ricardo Marimon
Ricardo Marimon

Reputation: 10697

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

    }

}

Upvotes: 1

marcosbeirigo
marcosbeirigo

Reputation: 11338

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.

Upvotes: 0

Matthew Flynn
Matthew Flynn

Reputation: 2238

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.

Upvotes: 4

crowne
crowne

Reputation: 8534

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.

Upvotes: 0

Mark Pope
Mark Pope

Reputation: 11274

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.

Upvotes: 1

Related Questions