同步实现:Java [英] Synchronized implementation : Java

查看:95
本文介绍了同步实现:Java的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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?

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

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

这是ProducerFixed类:

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

这是ConsumerFixed类:

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

这是Producer_Consumer_Fixed类:

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

推荐答案

QFixed if (flag) wait的获取和放置方法中使用的惯用法已损坏,您应该使用while循环.请参见有关受保护块的Oracle教程.

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.

一旦我更改了类的名称以删除"Fixed",并在Q类中将if替换为while,就像这样:

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

我从开始输出

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.

使用while循环是一件好事,有几个原因.

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

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

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.

while循环的另一个原因是仅仅因为线程退出等待状态并不一定意味着发生了通知.根据对象#的javadoc等待:

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
}

这来自JVM实现中的竞争条件;如文档所述,这应该很少发生.但这可能是问题的根源,在没有得到通知的情况下获得等待返回可能会导致出现多种情况,如您所见.

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.

这篇关于同步实现:Java的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆