繁体   English   中英

数据库关闭时阻止工作线程的最佳同步策略

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM