我有一个应用程序,每15分钟左右从远程数据库复制一次。它只是使两个存储库保持同步。一旦进行此复制,就不可能再次执行此操作。我已经设置了以下结构,但我不确定它是否是正确的方法。
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();
}
}
}
ReplicatorRunner
是拥有方法replicate
的类,一次只能运行一个。
编辑。 如果方法已在任何实例上运行,我需要下一次失败调用( not block )。
答案 0 :(得分:4)
这看起来不错。 ReentrantLock.tryLock()只会将锁定给一个线程,因此不需要synchronized
。它还可以防止您所说的同步中固有的阻塞。 ReentrantLock是Serializable,因此应该在整个群集中工作。
去吧。
答案 1 :(得分:1)
将public replicate()
更改为public synchronized replicate()
这种方式复制只允许一次访问一个线程。您还可以删除ReentrantLock和所有相关代码。
答案 2 :(得分:1)
我最终使用了以下内容:
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();
}
}
}
答案 3 :(得分:0)
查看信号量类here或将方法标记为已同步
在任何给定时间执行该方法的线程都拥有对它的锁定,从而避免其他线程调用该方法,直到其执行结束。
编辑:如果您希望其他线程失败,您可以使用Lock,并测试是否可以通过tryLock方法获得锁定。
答案 4 :(得分:0)
在不查看ReentrantLock的细节的情况下,我发现这种多个同时复制例程的防止将仅限于单个JVM实例。
如果该类的另一个实例在单独的JVM中启动,那么您可能遇到了麻烦。
为什么不在数据库上放置锁机制?即控制表中的一行,设置为描述复制是否正在运行的值,并在复制完成时重置该值。