【问题标题】:Synchronized implementation : Java同步实现:Java
【发布时间】:2014-12-19 11:11:34
【问题描述】:

这是一个消费者-生产者问题,我希望得到如下输出:
看跌:0
获取:0
放:1
获取:1
....等等。
但与此相反,尽管使用了 wait() 和 notify() 方法,但 Consumer 类多次消耗相同的 q 值。以及 Producer 类超出了消费者。如何获得同步输出?

这是 QFixed 类:(它定义了 put() 和 get() 方法)

class QFixed{
    int n;
    boolean valueset = false;

    synchronized int get(){
        if(!valueset){
            try {
                wait();
            } catch (InterruptedException ex) {
                System.out.println("interrupted");
            }
        }
        System.out.println("Got: " +n);
        valueset = false;
        notify();
        return n;
    }

    synchronized void put(int n){
        if (valueset){
            try {
                wait();
            } catch (InterruptedException ex) {
                System.out.println("interrupted");
            }
        }
        this.n = n;
        valueset = true;
        System.out.println("Put: "+n);
        notify();
    }
}

这是 ProducerFixed 类:

class ProducerFixed implements Runnable{
    Q q;
    Thread t;
    public volatile boolean flag = true;
        ProducerFixed(Q q){
            this.q = q;
            t = new Thread(this,"Producer");
            t.start();
        }

    @Override
        public void run(){
            int i =0 ;
            while(flag){
                q.put(i++);
            }
        }

    void stop() {
        flag = false;
    }
}

这是 ConsumerFixed 类:

class ConsumerFixed implements Runnable{
    Q q;
    Thread t;
    public volatile boolean flag = true;

        ConsumerFixed(Q q){
            this.q = q;
            t = new Thread(this,"Consumer");
            t.start();
        }

    @Override
        public void run(){
            while(flag){
                q.get();
            }
        }

    public void stop() {
        flag = false;
    }
}

这是 Producer_Consumer_Fixed 类:

public class Producer_Consumer_Fixed {
    public static void main(String arg[]){
        Q q = new Q();
        Producer p = new Producer(q);
        Consumer c = new Consumer(q);

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            System.out.println("interrupted");
        }

        p.stop();
        c.stop();
        try{
            p.t.join();
            c.t.join();
        }catch(InterruptedException e){
            System.out.println("interrupted");
        }
    }
}

【问题讨论】:

  • 请修复您代码中的编译错误,您的代码看起来不错..
  • 如何修复编译错误??
  • 您向我们展示的代码不能是您正在执行的代码。 new Q() Q 的类。
  • @JainamJhaveri - 你已经编写了程序。如何修复编译错误是什么意思?。你的班级名称是错误的..纠正他们..

标签: java multithreading producer-consumer thread-synchronization


【解决方案1】:

if (flag) wait 的 QFixed get 和 put 方法中使用的习语已损坏,您应该改用 while 循环。见the Oracle tutorial on guarded blocks

一旦我更改了类的名称以删除“Fixed”,并在 Q 类中将 if 替换为 while,如下所示:

class Q {
    int n;
    boolean valueset = false;

    synchronized int get(){
        while(!valueset){
            try {
                wait();
            } catch (InterruptedException ex) {
                System.out.println("interrupted");
            }
        }
        System.out.println("Got: " +n);
        valueset = false;
        notify();
        return n;
    }

    synchronized void put(int n){
        while (valueset){
            try {
                wait();
            } catch (InterruptedException ex) {
                System.out.println("interrupted");
            }
        }
        this.n = n;
        valueset = true;
        System.out.println("Put: "+n);
        notify();
    }
}

我得到了以

开头的输出
Put: 0
Got: 0
Put: 1
Got: 1
Put: 2
Got: 2
Put: 3
Got: 3
Put: 4
Got: 4
Put: 5
Got: 5
Put: 6
Got: 6
Put: 7
Got: 7
Put: 8
Got: 8
Put: 9
Got: 9
Put: 10
Got: 10
...

每个值都被放置和获取一次,这就是你想要的输出。

使用 while 循环是一件好事的原因有很多。

等待线程放弃监视器,一旦唤醒,它必须重新获取监视器,然后才能继续退出等待方法。这意味着其他线程可以使用监视器并可能更改同步保护的数据的状态。一旦线程重新获得了监视器,它需要再次检查条件,然后才能知道它认为它收到通知的条件是否真的发生了。否则线程会根据陈旧的信息来决定要做什么。

在涉及三个或更多竞争线程的示例中,这将是一个大问题。但是,对于这种特定情况,我看不到有问题的操作顺序。

while 循环的另一个原因是线程退出等待并不一定意味着通知发生了。根据javadoc for Object#wait

线程也可以在没有被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,如果条件不满足则继续等待。换句话说,等待应该总是在循环中发生,就像这样:

synchronized (obj) {
    while (<condition does not hold>)
        obj.wait(timeout);
    ... // Perform action appropriate to condition
}

这来自 JVM 实现中的竞争条件;就像文档说的那样,这应该是一种罕见的情况。但这可能是问题的根源,在没有得到通知的情况下等待返回可能会产生像您看到的那样多次获取的情况。

【讨论】: