3

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

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

  • 模拟一家商店提前订购甜甜圈

  • 一旦订购了5000个甜甜圈,该商店将不再接受订单

好吧,如果我应该编写 java 类来充当监视器,或者我应该使用 Java-Semaphore 类,我有点卡住了?

请给我建议。谢谢您的帮助。

4

3 回答 3

3

任何 java 对象都可以通过继承自的等待/通知方法作为监视器Object

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并进入睡眠状态。

对于更简化的方法,在BlockingQueue的各种实现处有一个循环,它提供了开箱即用的上述功能!

于 2012-04-23T10:03:52.630 回答
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
}

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

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


在实践中,我可能会使用AtomicInteger代替,它基本上是一个包装器,int以允许原子查询和更新,就像DonutShop上面的类一样。它还有一个优点,即它在最大限度地减少排他阻塞方面更有效,并且它是标准库的一部分,因此其他开发人员会比您自己编写的类更容易熟悉。

就正确性而言,两者都足够了。

于 2012-04-23T10:29:59.057 回答
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
}
于 2012-04-23T10:26:56.687 回答