如何在允许并发执行的同时防止两个操作相互交错?

时间:2014-05-27 15:42:47

标签: java multithreading concurrency

我有两种方法,foo()bar()。可能会有多个线程同时调用这些方法。如果同时运行foo()bar(),则可能会很麻烦,因为内部逻辑的交错会使系统处于不一致状态。但是,对于多个线程能够同时调用foo()并且多个线程能够同时调用bar(),它是完全正确的,并且实际上是可取的。最后一个条件是foo()预计会尽快返回,而bar()没有硬性时间限制。

我一直在考虑以各种方式控制此行为。以最简单的形式使用synchronized并不起作用,因为这将阻止对每个方法的并发调用。起初我认为ReadWriteLock可能是合适的,但这只允许其中一个方法与它自己同时调用。我考虑的另一种可能性是在两个单独的队列上排队这些方法的请求,并让一个消费者同时执行队列中的每个foo(),然后队列中的每个bar(),但这似乎是这样的很难调整,以避免不必要地阻止foo()

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

我认为一个好的解决方案是创建一个单独的类来控制对每个方法的访问。您将创建此类的单例,然后使用它来控制何时可以继续输入任一方法。

这是第三次迭代。这个可以防止饥饿。

用法可能在foo()调用之外:

em.enterFoo(Thread.currentThread());
foo();
em.exitFoo();

但是如果可能的话,foo()的入口和出口处的调用可能更干净。

代码:

public static class EntryManager
{
    private int inFoo = 0;
    private int inBar = 0;
    private Queue<Thread> queue = new LinkedList<>();

    public synchronized void enterBar(Thread t) throws InterruptedException
    {
        // Place the Thread on the queue
        queue.add(t);

        while(queue.peek() != t)
        {
            // Wait until the passed Thread is at the head of the queue.
            this.wait();
        }

        while(inFoo > 0)
        {
            // Wait until there is no one in foo().
            this.wait();
        }
        // There is no one in foo.  So this thread can enter bar.
        // Remove the thread from the queue.
        queue.remove();         
        inBar++;    
        // Wakeup everyone.
        this.notifyAll();
    }

    public synchronized void enterFoo(Thread t) throws InterruptedException
    {
        // Place the thread on the queue
        queue.add(t);

        while(queue.peek() != t)
        {
            // Wait until the passed Thread is at the head of the queue.
            this.wait();
        }

        while(inBar > 0)
        {
            this.wait();
        }
        // There is no one in bar.  So this thread can enter foo.
        // Remove the thread from the queue.
        queue.remove(); 
        inFoo++;
        // Wakeup everyone.
        this.notifyAll();
    }

    public synchronized void exitBar()
    {
        inBar--;
        // Wakeup everyone.
        this.notifyAll();
    }

    public synchronized void exitFoo()
    {
        inFoo--;
        // Wakeup everyone.
        this.notifyAll();
    }
}

答案 1 :(得分:1)

我不知道该问题的名称,所以我会编写自己的同步助手对象来处理它。它听起来像一个很多,就像一个读/写锁,除了读/写锁同时允许任意数量的读者,或者只是一个作者,但不是两个;你的锁会允许任意数量的foo()或任意数量的bar(),但不能同时允许两者。

棘手的部分是确保锁定是公平的。没有问题,如果没有争用,但如果锁在&#34; foo&#34;模式,有一个稳定的线程流,想要调用foo(),只有一两个想要调用bar()。 bar()线程如何运行?

实际上,它让我想起了繁忙的高速公路交叉口的很多红绿灯。交通信号灯可以让汽车在东/西航线上,或在北/南航线上流动,但不能两者兼而有之。你不希望光线过于频繁地切换,每个周期只让一两辆车通过,因为这样效率很低。但你也不希望光线让司机等待这么长时间才会生气。

我感觉政策可能必须根据您的特定应用进行定制。即,它可能取决于调用两个函数的频率,是否以突发方式调用等等。

我会从读取器/写入器锁的源代码开始,并尝试将其破解,直到它对我有效。