Java类作为监视器

时间:2012-04-23 10:02:09

标签: java multithreading concurrency

我需要编写一个java程序,但在开始之前我需要一些建议。

我要编写的程序是执行以下操作:

  • 模拟商店采用甜甜圈的高级订单

  • 一旦订购了5000个甜甜圈,商店就不会接受进一步的订单

好的,我有点想我是不是应该编写java-class来充当Monitor,还是应该使用Java-Semaphore类呢?

请指教。谢谢你的帮助。

3 个答案:

答案 0 :(得分:3)

任何java对象都可以通过从Object继承的wait / notify方法充当监视器:

Object monitor = new Object();

// thread 1    
synchronized(monitor) {
    monitor.wait();
}

// thread 2
synchronized(monitor) {
    monitor.notify();
}

请确保在调用这些方法时保持监视器对象上的锁定(不要担心wait,锁定会自动释放以允许其他线程获取它)。这样,您就可以在线程之间进行信号传输。

在我看来,您正在实施有限的生产者 - 消费者队列。在这种情况下:

  1. 制作人将继续将项目放入共享队列。
  2. 如果队列大小达到5000,它将在共享监视器上调用wait并进入休眠状态。
  3. 当它放置一个项目时,它会在监视器上调用notify以唤醒消费者,如果它正在等待。
  4. 消费者将继续从队列中取物品。
  5. 当它需要一个项目时,它会在监视器上调用notify来唤醒制作人。
  6. 如果队列大小达到0,则消费者会调用​​wait并进入休眠状态。
  7. 对于更简化的方法,在BlockingQueue的各种实现中有一个循环,它提供了开箱即用的上述功能!

答案 1 :(得分:1)

在我看来,本练习的核心是以线程安全和原子的方式更新计数器(所采取的订单数)。如果实施不当,您的商店可能会因为错过更新以及可能不同的线程看到柜台的陈旧价值而最多接受超过5000个预订。

以原子方式更新计数器的最简单方法是使用synchronized方法来获取和递增它:

class DonutShop {

    private int ordersTaken = 0;

    public synchronized int getOrdersTaken() {
        return ordersTaken;
    }

    public synchronized void increaseOrdersBy(int n) {
        ordersTaken += n;
    }

    // Other methods here
}

synchronized methods意味着任何时候只有一个线程可以调用任一方法(并且它们还提供内存屏障以确保不同的线程看到相同的值而不是可能过时的本地缓存的线程)。这样可以确保应用程序中所有线程的计数器视图一致。

(注意我没有“set”方法而是“increment”方法。“set”的问题是如果客户端必须调用shop.set(shop.get() + 1);,则另一个线程可能会增加调用getset,因此这个更新将会丢失。通过使整个增量操作成为原子 - 因为它在同步块中 - 这种情况不会发生。


在实践中,我可能会使用AtomicInteger,它基本上是int的包装器,允许原子查询和更新,就像上面的DonutShop类一样。它还具有在最小化独占阻塞方面更有效的优势,并且它是标准库的一部分,因此对于其他开发人员来说,比您自己编写的类更加熟悉。

就正确性而言,要么就足够了。

答案 2 :(得分:0)

就像Tudor写的那样,你可以使用任何对象作为监视器来进行通用锁定和同步。

但是,如果您要求在任何时候只能处理x个订单(对于您的情况为x = 5000),则可以使用java.util.concurrent.Semaphore类。它专门针对只能运行固定数量的作业的用例 - 在Semaphore

术语中称为许可

如果您立即进行处理,可以使用

private Semaphore semaphore = new Semaphore(5000);

public void process(Order order)
{
    if (semaphore.tryAcquire())
    {
        try
        {
            //do your processing here
        }
        finally
        {
            semaphore.release();
        }
    }
    else
    {
        throw new IllegalStateException("can't take more orders");
    }
}

如果if需要更多(需要人工输入,启动另一个线程/进程等),则需要在处理结束时添加回调,例如:

private Semaphore semaphore = new Semaphore(5000);

public void process(Order order)
{
    if (semaphore.tryAcquire())
    {
        //start a new job to process order
    }
    else
    {
        throw new IllegalStateException("can't take more orders");
    }
}

//call this from the job you started, once it is finished
public void processingFinished(Order order)
{
    semaphore.release();
    //any other post-processing for that order
}