我需要编写一个 java 程序,但在我自己开始之前我需要一些建议。
我将要编写的程序是执行以下操作:
模拟一家商店提前订购甜甜圈
一旦订购了5000个甜甜圈,该商店将不再接受订单
好吧,如果我应该编写 java 类来充当监视器,或者我应该使用 Java-Semaphore 类,我有点卡住了?
请给我建议。谢谢您的帮助。
我需要编写一个 java 程序,但在我自己开始之前我需要一些建议。
我将要编写的程序是执行以下操作:
模拟一家商店提前订购甜甜圈
一旦订购了5000个甜甜圈,该商店将不再接受订单
好吧,如果我应该编写 java 类来充当监视器,或者我应该使用 Java-Semaphore 类,我有点卡住了?
请给我建议。谢谢您的帮助。
任何 java 对象都可以通过继承自的等待/通知方法作为监视器Object
:
Object monitor = new Object();
// thread 1
synchronized(monitor) {
monitor.wait();
}
// thread 2
synchronized(monitor) {
monitor.notify();
}
只需确保在调用这些方法时持有监视器对象上的锁(不用担心wait
,锁会自动释放以允许其他线程获取它)。这样,您就有了一种方便的机制来在线程之间发出信号。
在我看来,您正在实现一个有界的生产者-消费者队列。在这种情况下:
wait
共享监视器并进入睡眠状态。notify
监视器来唤醒正在等待的消费者。notify
监视器来唤醒生产者。wait
并进入睡眠状态。对于更简化的方法,在BlockingQueue的各种实现处有一个循环,它提供了开箱即用的上述功能!
在我看来,这个练习的核心是以线程安全和原子的方式更新计数器(接受的订单数)。如果实施不正确,您的商店最终可能会收到超过 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);
,另一个线程可能会在调用get
and之间增加值set
,所以这次更新会丢失。通过使整个增量操作原子化——因为它在同步块中——这种情况不会发生。
在实践中,我可能会使用AtomicInteger代替,它基本上是一个包装器,int
以允许原子查询和更新,就像DonutShop
上面的类一样。它还有一个优点,即它在最大限度地减少排他阻塞方面更有效,并且它是标准库的一部分,因此其他开发人员会比您自己编写的类更容易熟悉。
就正确性而言,两者都足够了。
就像 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
}