【问题标题】:Unexpected result return when implementing wait and notify执行等待和通知时返回意外结果
【发布时间】:2016-03-23 03:37:34
【问题描述】:

我已经做了一个等待和通知示例程序,它是蛋糕店的抽象。有一个带有角色蛋糕机的线程用于生产蛋糕,一个带有角色服务员的线程用于交付蛋糕。我的期望是每次CakeMachine 类完成制作蛋糕时,它会向Waiter 类发送通知以进行交付。当我运行一个生产 3 个蛋糕的程序时,结果显示只交付了一个蛋糕。这是我的代码:

用于创建蛋糕对象的Cake 类:

class Cake {
private int weight;
private String color;

public Cake(int weight, String color) {
    this.weight = weight;
    this.color = color;
    }

public String toString(){
    return "The cake is in " + color  +" and is " + weight + " gram ";

    }
}

用于制作蛋糕的CakeMachine 类:

class CakeMachine implements Runnable{


private List<Cake> listCake;

CakeMachine(List<Cake> listCake) {
    this.listCake = listCake;
}

public void  makeCake() {
        int weight = new Random().nextInt(20);
        Cake cake = new Cake(weight, "color code is  " + weight );
        listCake.add(cake);
        System.out.println("cake has been cooked ");
        listCake.notify();
}

@Override
public void run() {

        for (int i = 0; i < 3; i++) {
           synchronized (listCake) {
               makeCake();
           }
        }
    }
}

Waiter 送蛋糕类:

class Waiter implements Runnable {

private List<Cake> listCake;

Waiter(List<Cake> listCake) {
    this.listCake = listCake;
}

public void delivery() {
        System.out.println("Waiter is waiting for the cake ");

            try {
                listCake.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         Cake cake = listCake.get(listCake.size() - 1);
         System.out.println(cake.toString() + "has been delivered to customers ");
}

@Override
public void run() {

    for (int i = 0; i < 3; i++)
    {
        synchronized (listCake) {
            delivery();
        }
    }
  }
}

还有主类:

public class WaitAndNotify {

    public static List<Cake> listCake = new ArrayList<>();

    public static void main(String[] args) {

    Thread waiter = new Thread(new Waiter(listCake));
    Thread cakeMachine = new Thread(new CakeMachine(listCake));
    waiter.start();
    cakeMachine.start();

    }
}

运行程序的结果是:

Waiter is waiting for the cake 
cake has been cooked 
cake has been cooked 
cake has been cooked 
The cake is in color code is  18 and is 18 gram has been delivered to   customers 
Waiter is waiting for the cake 

请帮助我了解这种情况。

【问题讨论】:

  • Effective Java Item 69: "很少有理由在新代码中使用waitnotify"
  • 你应该使用队列而不是列表,并使用并发包中的ConcurrentLinkedQueue来忽略多线程问题。
  • 通知线程(在这种情况下为等待者)不会自动重新获取通知锁定。在通知之后,Waiter 和 CakeMachine 线程必须竞争锁,在您的情况下,CakeMachine 会获得锁。

标签: java multithreading wait synchronized notify


【解决方案1】:

两大要点:

  • 您无法控制何时运行哪个线程。

  • 如果没有其他线程碰巧在该监视器上等待,则通知无效。您的服务员停止等待,蛋糕制作可以继续进行,并在服务员再次获取监视器之前完成。

在您的示例中,调度程序决定先运行一次服务员迭代,然后运行所有 3 次蛋糕制作迭代,然后在所有蛋糕制作完成后运行接下来的两次等待迭代。您可以引入标志来指示蛋糕何时准备好以及服务员何时等待,并让蛋糕制作延迟通知直到服务员出现,但尝试让线程同步执行确实是错误的,它击败了目的是将活动分成各自的线程。

添加条件标志后,交付方式将如下所示:

public void delivery() {
    System.out.println("Waiter is waiting for the cake ");
    waiterInPosition = true;
    try {
        while (!(cakeQueued)) {
            listCake.wait();
        }
        waiterInPosition = false;
        cakeQueued = false;
        Cake cake = listCake.get(listCake.size() - 1);
        System.out.println(cake.toString() 
        + "has been delivered to customers ");
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // restore interrupt flag
    }
}

但这是一个玩具示例,非常尴尬。将其建模为生产者 - 消费者问题会更自然,其中蛋糕制造商将蛋糕添加到队列中并且服务员将蛋糕从队列中取出。您可以尝试制作自己的阻塞队列,您的队列方法可以处理等待和通知 - 让您的数据结构处理同步和阻塞比让工作任务执行它要自然得多。这将像您当前的示例一样教会您如何使用等待和通知,同时更能代表现实世界的并发模式。

Oracle tutorial on guarded blocks 包含一个阻塞队列示例,关于在带有条件变量的循环中等待的建议是必不可少的,请阅读它。

【讨论】:

  • 我真的不明白为什么要连续运行 3 次蛋糕制作迭代。因为每次调用 notify() 方法时,都要唤醒一个“Waiter”线程,并执行 delivery() 方法。你能更详细地解释一下吗?谢谢。
  • @programer310:希望添加的解释有帮助
  • 第二个重点,你说服务员停止等待,蛋糕制作可以继续进行。但是服务员怎么可能停止等待。他们说当调用wait()时,线程会无限等待,直到收到来自其他线程的notify(),不是吗?
  • @programer310: 是的,如果你的线程在没有超时的情况下等待并且没有任何通知它可以无限期地继续等待。
  • 好吧,最后,在我的例子中出现了意外的结果,因为在 wait() 实际出现之前调用了三个 notify() 方法,不是吗?
【解决方案2】:

这可以通过典型的生产者消费者问题来解决

蛋糕课:

class Cake {
private int weight;
private String color;

public Cake(int weight, String color) {
    this.weight = weight;
    this.color = color;
}

public String toString() {
    return "The cake is in " + color + " and is " + weight + " gram ";

}
}

CakeMachine 既是生产者又是消费者,它同时具有交付蛋糕和制作蛋糕的功能

class CakeMachine {

private LinkedList<Cake> listCake = new LinkedList<Cake>();;
private Random random = new Random();
private static int LIMIT = 5;

public void makeCake() throws InterruptedException {
    while (true) {
        synchronized (this) {
            while (listCake.size() == LIMIT) {
                wait();
            }
            int weight = random.nextInt(20);
            Cake cake = new Cake(weight, "color code is  " + weight);
            listCake.add(cake);
            System.out.println("cake has been cooked ");
            notify();
        }
    }
}

public void deliverCake() throws InterruptedException {
    while (true) {
        synchronized (this) {
            Thread.sleep(random.nextInt(1000)); // on average 500 ms
            while (listCake.size() == 0) {
                System.out.println("Waiter is waiting for the cake ");
                wait();
            }

            Cake cake = listCake.removeFirst();
            System.out.println(cake.toString() + "has been delivered to customers ");
            notify();
        }
    }
}

}

现在是 final 和 main 类

public class CakeThreading {

public static void main(String[] args) {

    final CakeMachine cm = new CakeMachine();
    Thread cakeproducer = new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                cm.makeCake();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    });

    Thread waiter = new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                cm.deliverCake();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    });

    cakeproducer.start();
    waiter.start();

    try {
        cakeproducer.join();
        waiter.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

这将产生如下输出:

cake has been cooked 
cake has been cooked 
The cake is in color code is  2 and is 2 gram has been delivered to customers 
The cake is in color code is  14 and is 14 gram has been delivered to customers 
cake has been cooked 
cake has been cooked 
cake has been cooked 
The cake is in color code is  13 and is 13 gram has been delivered to customers 
The cake is in color code is  7 and is 7 gram has been delivered to customers 
The cake is in color code is  19 and is 19 gram has been delivered to customers 
Waiter is waiting for the cake 
cake has been cooked 
cake has been cooked 
cake has been cooked 
The cake is in color code is  12 and is 12 gram has been delivered to customers 
The cake is in color code is  2 and is 2 gram has been delivered to customers 
The cake is in color code is  2 and is 2 gram has been delivered to customers 
cake has been cooked 
cake has been cooked 

【讨论】:

    猜你喜欢
    • 2013-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多