Consumer Producer在java中不起作用

时间:2014-05-27 13:06:30

标签: java synchronization

我试图根据官方的oracle示例(http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html)创建一个消费者生产者。

这个解决方案给我带来了一个问题,因为某种程度上消耗(或产生)被执行两次。

示例输出:

0.27621192120612414

0.24838246527492802,0.30404137713732027

0.8848100139189661

0.9910138279470992,0.778606199890833

0.17368370874476935,0.5661899414440023 .......

我的代码与发布的oracle非常相似:

import java.util.Random;

public class Consumer implements Runnable {

    private SharedData sharedData;

    public Consumer(SharedData sharedData) {
    this.sharedData = sharedData;
    }

    @
    Override
    public void run() {
    // TODO Auto-generated method stub
    for (int i = 0; i < 10; i++) {

        double result = this.sharedData.calc();
        System.out.println(result);

        try {
        Thread.sleep(20);
        } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
    }
    }

public class Producer implements Runnable {

    private SharedData sharedData;

    private Random numGenerator = new Random();

    public Producer(SharedData sharedData) {
        this.sharedData = sharedData;
    }

    @
    Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 10; i++) {
        double firstNum = numGenerator.nextDouble();
        double secondNum = numGenerator.nextDouble();

        this.sharedData.store(firstNum, secondNum);
        System.out.println(firstNum + ", " + secondNum);

        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        }
    }
}

public class SharedData {

    private double x = 0;
    private double y = 0;

    private boolean empty = true;

    public synchronized double calc() {
        while (empty) {
        try {
            wait();
        } catch (InterruptedException e) {}
        }
        empty = true;
        notifyAll();
        return (x + y) / 2;
    }

    public synchronized void store(double x, double y) {
        while (!empty) {
        try {
            wait();
        } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = false;
        this.x = x;
        this.y = y;
        notifyAll();
    }
}

2 个答案:

答案 0 :(得分:1)

您的System.out.println次呼叫不在任何同步部分中,因此即使生产者在消费者消费之前已经生产,也不能保证其输出将在消费者输出之前出现:

  1. 生产者线程生成,消费者线程等待
  2. 消费者线程消费
  3. 消费者线程打印结果
  4. 生产者线程打印结果
  5. System.out.println调用放在同步部分中,您应该看到消费者和生产者输出之间存在严格的交替。

答案 1 :(得分:0)

您所看到的问题不是保护系统的故障,而是您的打印机制失败。

以下是您可以使用的示例。我创建了一个名为Calc的新类,它在计算时保存计算的详细信息。它表明系统工作正常:

public class Consumer implements Runnable {

    private SharedData sharedData;

    public Consumer(SharedData sharedData) {
        this.sharedData = sharedData;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 10; i++) {

            Calc result = this.sharedData.calc();
            System.out.println(result);

            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

public class Producer implements Runnable {

    private SharedData sharedData;

    private Random numGenerator = new Random();

    public Producer(SharedData sharedData) {
        this.sharedData = sharedData;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 10; i++) {
            double firstNum = numGenerator.nextDouble();
            double secondNum = numGenerator.nextDouble();

            this.sharedData.store(firstNum, secondNum);
            //System.out.println("Sum: " + firstNum + ", " + secondNum);

            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

}

public class Calc {

    final double x;
    final double y;
    final double r;

    public Calc(double x, double y, double r) {
        this.x = x;
        this.y = y;
        this.r = r;
    }

    public String toString() {
        return x + "," + y + "=" + r;
    }
}

public class SharedData {

    private double x = 0;
    private double y = 0;

    private volatile boolean empty = true;

    public synchronized Calc calc() {
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        // Create my Calc before the data can be changed.
        Calc calc = new Calc(x, y, (x + y) / 2);
        empty = true;
        notifyAll();
        return calc;
    }

    public synchronized void store(double x, double y) {
        while (!empty) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        // Toggle status.
        empty = false;
        this.x = x;
        this.y = y;
        notifyAll();
    }

}

public void test() throws InterruptedException {
    System.out.println("Hello");
    SharedData data = new SharedData();
    Thread pThread = new Thread(new Producer(data));
    Thread cThread = new Thread(new Consumer(data));
    pThread.start();
    cThread.start();
    pThread.join();
    cThread.join();
}

我明白了:

0.6724169142503265,0.6079999474086301=0.6402084308294782
0.6821043385454105,0.08553214712777224=0.3838182428365914
0.9527912599940244,0.4659048372170267=0.7093480486055255
0.23726296551963733,0.09811671486141438=0.16768984019052585
0.192398049915115,0.7641124370868287=0.47825524350097187
0.014149497154971824,0.07331728450626529=0.04373339083061856
0.7143483172261206,0.532969217817149=0.6236587675216347
0.3660578187157878,0.5246494194965169=0.44535361910615234
0.8368831236536345,0.9004030373411539=0.8686430804973941
0.6986966105042433,0.4459182041312596=0.5723074073177514

就我所见,这很好。

注意 - 然而 - 一旦您在empty中将true设置为calc,其他线程就可以修改xy。您应该在设置empty标志之前 之前进行计算。

另请注意,您的empty标记应为volatile