java - 多线程:为什么我的程序终止时有不同的结果?

标签 java multithreading thread-safety

我正在研究多线程并尝试编写程序。

我编写了一个餐厅程序,模拟并行为多个顾客提供服务:

  • 一家餐厅开业,创建一名服务员、一名厨师和一些顾客,并等待所有顾客都吃完饭
  • 顾客下了订单,等待他的 boolean 值“吃过”变为真,然后通知餐厅
  • 服务员等待顾客下单,然后通知厨师
  • 厨师等待服务员通知他有关订单的信息,准备餐点并将顾客的“已吃”设置为 true

不知何故,我的程序将以大致不同的结果终止。

经过我的研究,我可以看到以不同方式终止的两个原因: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();
        }
    }
}

whole code

最佳答案

答案问题是这样的

synchronized(this){
...
}

上面的代码不正确有两个原因。

  1. 不存在相互排斥。每个线程都有自己的监视器/锁。你的锁可以被它自己的线程看到。因此synchronized(this)是多余的。
  2. 您永远不应该在 Thread 实例上进行同步(不好的做法)。在你的情况下,这是 Thread 的实例。 还有一件事不要扩展线程,尽量避免使用 Runnable 代替

如何解决?

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 它可能最适合您的目的

即不是共享互斥对象而是共享 ArrayBlockingQueue 实例。它将照顾 如果订单队列为空或客户队列已满,许多类似的事情都会等待

关于java - 多线程:为什么我的程序终止时有不同的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17645662/

相关文章:

c++ - "C++ Concurrency in Action"中的阿姆达尔定律

Java HashTable size() 后跟 value() 在线程中安全吗?

java - OpenJDK 11 是否支持 Java Flight Recorder?

java - Android ksoap2 通过 https

java - 为什么在 android studio 中执行 osmdroid.jar 文件会抛出错误?

c# - C# 的多线程和并发

java - 简单的JSP网站

multithreading - Play Framework Scala 线程亲和性

java - 第二个线程如何检查和重新检查第一个线程当前使用的类的锁对象?

c++ - std::thread调用类方法