同步实现:Java

时间:2014-12-19 11:11:35

标签: java multithreading producer-consumer thread-synchronization

这是消费者 - 生产者问题,我希望得到如下输出:
投入:0
得到:0
放:1
得到:1
....等等。
但与此相反,Consumer类多次使用q的相同值,尽管使用了wait()和notify()方法..以及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");
        }
    }
}

1 个答案:

答案 0 :(得分:4)

if (flag) wait的QFixed get和put方法中使用的习惯用法被破坏了,你应该使用while循环。请参阅the Oracle tutorial on guarded blocks

我更改了类的名称以删除'固定',并在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循环的另一个原因是,仅仅因为线程退出等待并不一定意味着发生了通知。根据{{​​3}}:

  

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

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

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