[英]Best synchronization strategy to block worker threads when a database is down
多个工作人员正在从队列中进行处理,当数据库发生故障时,它将联系一个主管,该主管随后会锁定所有工作线程并以一定间隔轮询数据库,直到它启动,然后它将释放所有线程,以便它们可以继续处理。 工作线程可以前进或等待处理,并且管理员线程可以锁定或解锁。
我在考虑这样的界面。 你会使用什么同步原语? 演员将是一个很好的解决方案,但我没有时间重写。
public interface Latch {
/**
* This method will cause a thread(s) to only advance if the latch is in an open state. If the
* latch is closed the thread(s) will wait until the latch is open before they can advance.
*/
void advanceWhenOpen();
/**
* Close the latch forcing all threads that reaches the latch's advance method to wait until
* its open again.
*/
void close();
/**
* Opens the latch allowing blocked threads to advance.
*/
void open();
boolean isOpen();
}
你想要的并不是真正的“闩锁” - 至少“实践中的Java并发”一书中说“一旦闩锁到达终端状态,它就不能再次改变状态,所以它永远保持开放状态。”
但是你可以在后台使用CountDownLatch对象 - 每当你的“Latch”需要关闭时,你就可以在advanceWhenOpen()中创建一个计数为1且await()的新CountDownLatch对象。 我认为从可读性的角度来看,这将是最好的解决方案。
为此,我将使用ReadWriteLock作为同步原语。 与简单的监视器或互斥锁相反,读/写锁的优点是多个线程可以在任何给定时间保持读锁定。 当你有很多读者(例如你的线程池)和只有一个或几个编写器(例如线程检查数据库的打开/关闭)时,这是有利的。
使用单个监视器或互斥锁,您的线程将在一个锁上序列化,使该部分代码有争议。
一种选择是在数据库不可用时代理队列以使其可以使用。 工作人员可以在处理时检查队列的暂停状态,并在必要时等待它取消暂停。 一个基本的代码演示:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
public class PausableQueue<T> {
LinkedBlockingQueue<T> q = new LinkedBlockingQueue<T>();
AtomicReference<CountDownLatch> pause = new AtomicReference<CountDownLatch>(new CountDownLatch(0));
public T take() throws InterruptedException {
awaitPause();
return q.take();
}
public void awaitPause() throws InterruptedException {
pause.get().await();
}
public void setPaused(boolean paused) {
if (paused) {
// only update if there are no threads waiting on current countdown-latch
if (!isPaused()) {
pause.set(new CountDownLatch(1));
}
} else {
pause.get().countDown();
}
}
public boolean isPaused() {
return (pause.get().getCount() > 0L);
}
/* *** Test the pausable queue *** */
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
try {
testPause(executor);
} catch (Exception e) {
e.printStackTrace();
}
executor.shutdownNow();
}
private static void testPause(ExecutorService executor) throws Exception {
final PausableQueue<Object> q = new PausableQueue<Object>();
for (int i = 0; i < 3; i++) {
q.q.add(new Object());
}
final CountDownLatch tfinished = new CountDownLatch(1);
Runnable taker = new Runnable() {
@Override
public void run() {
println("Taking an object.");
try {
Object o = q.take();
println("Got an object: " + o);
} catch (Exception e) {
e.printStackTrace();
} finally {
tfinished.countDown();
}
}
};
executor.execute(taker);
tfinished.await();
final CountDownLatch tstarted2 = new CountDownLatch(2);
final CountDownLatch tfinished2 = new CountDownLatch(2);
taker = new Runnable() {
@Override
public void run() {
println("Taking an object.");
tstarted2.countDown();
try {
Object o = q.take();
println("Got an object: " + o);
} catch (Exception e) {
e.printStackTrace();
} finally {
tfinished2.countDown();
}
}
};
q.setPaused(true);
println("Queue paused");
executor.execute(taker);
executor.execute(taker);
tstarted2.await();
// Pause to show workers pause too
Thread.sleep(100L);
println("Queue unpausing");
q.setPaused(false);
tfinished2.await();
// "Got an object" should show a delay of at least 100 ms.
}
private static void println(String s) {
System.out.println(System.currentTimeMillis() + " - " + s);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.