我正在研究多线程并尝试编写程序。
我写了一个餐厅程序,模拟并行服务多个客户:
不知何故,我的程序会以大致不同的结果终止。
经过研究我已经完成了,我可以看到两种不同的终止原因:1)如果我的方法不同步(在我的程序中不是这种情况)。 2)因为我们不能影响线程资源的分配方式,但这会导致线程序列的一些细微差别
但我的程序终止时存在很大的差异,而不仅仅是线程序列的细微差别:
有人可以给我任何提示吗?
主厨代码:
class Chef extends Thread{
private static int _id=1;
private int id;
Order order;
public Chef(){
this.id=_id;
_id++;
order=null;
this.start();
}
@Override
public void run() {
System.out.println("Chef ("+id+") starts to work...");
synchronized(this){
while(order==null){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("Chef ("+id+") prepared Order ("+this.order.getId()+")");
Restaurant.customers.get(this.order.getId()-1).served=true;
synchronized(Restaurant.customers.get(this.order.getId()-1)){
Restaurant.customers.get(this.order.getId()-1).notify();
}
order=null;
}
public void prepareOrder(Order order){
this.order=order;
System.out.println("Chef ("+this.id+") prepares order ("+order.getId()+")");
synchronized(this){
this.notify();
}
}
}
服务员的代码(正常工作,总是收到传入的订单):
class Waiter extends Thread{
private static int _id=1;
private int id;
Order order;
public Waiter(){
this.id=_id;
_id++;
order=null;
this.start();
}
@Override
public void run() {
System.out.println("Waiter ("+this.id+") starts to work...");
synchronized(this){
while(takenOrder==false){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
order=null;
Restaurant.chefs.get(0).prepareOrder(order);
}
public void takeOrder(Order order){
this.order=order;
System.out.println("Waiter ("+this.id+") takes order ("+this.order.getId()+")");
synchronized(this){
this.notify();
}
}
}
答案 0 :(得分:2)
回答问题是这个
synchronized(this){
...
}
上述代码不正确有两个原因。
如何解决?
class Chef implments Runnable {
private Object lock;
Chef(Object lock) {
this.lock = lock;
}
public run() {
synchronized(lock) {
// do stuff here
}
}
}
class Waiter implments Runnable {
private Object lock;
Chef(Object lock) {
this.lock = lock;
}
public run() {
synchronized(lock) {
// do stuff here
}
}
}
//your main
public static void main(String []args) {
Object obj = new Object();
Thread chef = new Thread(new Chef(obj));
Thread waiter = new Thread(new Waiter(obj));
chef.start();
waiter.start();
}
建议上述方法是两个线程之间互斥的一个非常基本的例子。 但它不是最好的方法。尝试使用BlockingQueue它可能最适合您的目的
即代替共享mutex obj共享ArrayBlockingQueue实例。它会照顾 如果订单队列为空或客户队列已满,
等许多类似的东西会等待答案 1 :(得分:1)
问题不是理论上的问题,显然代码中存在一些不正确的问题。
据推测,我的猜测是Chef
在等待第二次Waiter
通知之前没有检查现有订单。
情景:
答案 2 :(得分:1)
监视锁仅适用于Object
的单个共享实例。你的厨师和服务员没有使用相同的外观,因此实际上并没有相互采取
实际上更多的是侥幸,厨师在无限期阻止之前获得订单。
创建一个单独的对象(可能是ORDER_LOCK
),服务员用它来告诉检查是否有可用的订单。
如果有一个或多个订单,服务员会在此锁定时调用notify
,并在此锁定时检查wait
。
使其成为public static final
,以确保两者使用相同的锁定实例
<强>更新强>
我发现有一些奇怪的东西,但不要偏离......
您的厨师依赖于一个名为takenOrder
的州旗。该标志可以由多个线程同时修改。也没有办法阻止厨师接受两个订单。
即
这被称为竞争条件。在处理支票之前,预期结果(订单(1))正在改变。
您可以通过ID生成实际看到这一点。我能够使用相同的ID生成两个订单
您真正需要的是某种排队系统,其中自定义无法下订单,直到服务员可以接受它为止。
服务员可能在(服务员)队列中,接受订单并发送订单。
厨师可以在(厨师)排队或准备订单。
客户实际上并不在意。他们将做出决定(订单),等待服务员下订单,等待订单,吃饭或离开。
如果对象无效,则该对象只能在队列中。所以它离开队列开始它的工作并在它完成后返回。
订单与客户之间也没有联系......那么您如何知道哪个订单属于哪个客户?
现在,根据您想要实现的目标,您可以创建自己的阻塞队列,例如......
private List<Waiter> waiters;
//...//
public Waiter getNextAvailableWaiter() {
Waiter waiter = null;
synchronized (WAITER_QUEUE_LOCK) {
while (waiters.isEmpty()) {
WAITER_QUEUE_LOCK.wait();
}
waiter = waiters.remove(0);
}
return waiter;
}
或者使用JDK中提供的其中一种实现...有关详细信息,请参阅BlockingQueue。
现在,方法;)
就个人而言,没有一个实体应该彼此直接相关。每个都应该由餐厅管理。
例如。当客户准备好时,它应该要求nextAvailableWaiter
。当一个可用时,客户将向服务员发出订单。服务员要么获得nextAvailableChef
并向他们下订单,要么更好,将订单放入订单队列。
当一位厨师可以使用时,他们将获得nextOrder
并准备它。一旦准备好,它应放在一个orderReady
队列中,放置它的服务员或下一个可用的服务员可以将它交付给客户......