Synchronized implementation : Java

末鹿安然 提交于 2019-12-11 02:35:30

问题


This is a Consumer-Producer problem in which, I wish to get output as follows:
Put: 0
Get: 0
Put: 1
Get: 1
....and so on.
But in contrast to this, the Consumer class consumes same value of q multiple times, inspite of using wait() and notify() methods.. as well as the Producer class overruns the consumer. How can I get synchronized output?

This is QFixed class:(which defines put() and get() methods)

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();
    }
}

This is ProducerFixed class:

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;
    }
}

This is ConsumerFixed class:

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;
    }
}

This is Producer_Consumer_Fixed class:

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:


The idiom used in your QFixed get and put methods of if (flag) wait is broken, you should be using a while loop instead. See the Oracle tutorial on guarded blocks.

Once I changed the names of the classes to remove 'Fixed', and replaced if with while in the Q class, like this:

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();
    }
}

I got output starting with

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
...

where each value gets put and gotten once, which is the output you want.

There are several reasons why using the while loop is a good thing.

A waiting thread gives up the monitor, once it wakes up it has to reacquire the monitor before it can proceed with exiting the wait method. That means that other threads can take the monitor and possibly change the state of the data that the synchronization is protecting. Once the thread has reacquired the monitor it needs to check the condition again before it can know whether the condition that it thinks it got notified for actually happened. Otherwise the thread is deciding what to do based on stale information.

In examples involving three or more contending threads this would be a big issue. However, for this specific case I don't see an ordering of operations that is problematic.

Another reason for the while loop is that just because a thread exits from waiting doesn't necessarily mean a notification took place. According to the javadoc for Object#wait:

A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:

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

This comes from a race condition in the JVM implementation; like the documentation says, it should be a rare occurrence. But it could be the source of the problem here, having the wait return without having been notified could produce a situation with multiple gets like what you saw.



来源:https://stackoverflow.com/questions/27564844/synchronized-implementation-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!