Java类作为监视器

3
我需要编写一个Java程序,但在开始之前我需要一些建议。
我将要编写的程序需要实现以下功能:
- 模拟一个商店接受预定甜甜圈的过程。 - 一旦预定的甜甜圈数量达到5000个,商店将不再接受进一步的订单。
我有些困惑,不知道应该编写Java类来充当监视器,还是应该使用Java Semaphore类?
请给我建议。感谢您的帮助。
3个回答

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并进入睡眠状态。
要使用更简化的方法,请查看各种实现BlockingQueue,它可以提供上述功能!

谢谢您的快速回复。好的,使用Java-CyclicBarrier类编写是否是更好的选择呢? - JasonMayhem
@JasonMayhem:不是很合适,CyclicBarrier构造似乎更适用于主从模式(即一组线程完成一些工作,然后等待彼此完成),而不是生产者-消费者。对于这种情况,您可以使用BlockingQueue的实现。 - Tudor
谢谢Tudor。我猜从BlockingQueue的链接中,这可以很容易地通过编写充当生产者和消费者的类来完成。那么我可以跳过监视器实现了吗? - JasonMayhem

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);,则另一个线程可能已经在getset之间增加了该值,因此此更新将丢失。通过使整个增量操作成为原子操作 - 因为它在同步块中 - 不会发生这种情况。


实际上,我可能会使用AtomicInteger,它基本上是一个围绕着int的包装器,允许原子查询和更新,就像上面的DonutShop类一样。 它还有一个优点,即在最小化排他性阻塞方面更有效,并且它是标准库的一部分,因此与您自己编写的类相比,其他开发人员会更加熟悉它。
就正确性而言,两者都足够。

感谢您的帮助,Andrzej。非常感激。 - JasonMayhem

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");
    }
}

如果需要更多的操作(需要人工输入、启动另一个线程/进程等),您需要添加回调函数以便在处理完成后进行通知,例如:
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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接