老师的源码
public class Producer extends Thread {
private Tray tray; private int id;
public Producer(Tray t, int id) {
tray = t; this.id = id; }
public void run() {
for (int i = 0; i < 10; i++)
for(int j =0; j < 10; j++ ) {
tray.put(i, j);
System.out.println("Producer #" + this.id + " put: ("+i +","+j + ").");
try { sleep((int)(Math.random() * 100)); }
catch (InterruptedException e) { }
};
}
}
public class Consumer extends Thread {
private Tray tray;
private int id;
public Consumer(Tray t, int id) {
tray = t; this.id = id; }
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = tray.get();
System.out.println("Consumer #" + this.id + " got: " + value);
}
}
}
public class Tray {
private int x,y; private boolean available = false;
public synchronized int get() {
while (available == false) {
try { wait(); } catch (InterruptedException e) { } }
available = false; // 此时available为真,确保所有其他消费者等待
notifyAll();
return x+y; }
public synchronized void put(int a, int b) {
while (available == true) {
try { wait(); } catch (InterruptedException e) { } }
available = true; // 唤醒等待队列中的其他消费者或生产者
x= a; y = b;
notifyAll(); }
}
public class ProducerConsumerTest {
public static void main(String[] args) {
Tray t = new Tray();
Producer p1 = new Producer(t, 1);
Consumer c1 = new Consumer(t, 1);
p1.start();
c1.start();
}
}
今天看了一下老师给的代码,跑了一下发现会有bug:即生产者的产品输出会在消费者的购买的输入之后(部分),尝试了几种改的方法
我最终的改法
import java.lang.Thread;
class Producer extends Thread {
private Tray tray;
private int id;
public Producer ( Tray t, int id){
tray = t;
this.id = id;
//new Thread(this,"Producer");
}
public void run () {
for( int i = 0; i < 10; i++) {
for( int j = 0; j < 10; j++) {
synchronized(tray){
tray.put(i, j);
System.out.println("Producer #" + id + "put:(" + i + "," + j + ").");
}
try{
sleep(100); // 这个sleep可以去掉的,不影响
} catch( InterruptedException e ) {
System.out.println("Producer Interrupted");
}
}
// System.out.println("hhhh");
}
}
}
class Consumer extends Thread {
private Tray tray;
private int id;
public Consumer ( Tray t, int id ) {
tray = t;
this.id = id;
//new Thread(this,"Consumer");
}
public void run () {
int value = 0;
for( int i = 0; i < 100; i++) {
synchronized (tray) {
value = tray.get();
System.out.println("Consumer #" + id + "got: " + value);
// try{
// sleep(10);
// } catch ( InterruptedException e ) {
// System.out.println("Consumer Interrupted");
}
}
}
}
class Tray {
private int x;
private int y;
private boolean available = false;
public synchronized int get() {
while ( available == false ) {
try{
wait();
} catch( InterruptedException e ) {
System.out.println("Tray Wrong");
}
}
//System.out.println("Consumer #" + "got: " + (x+y));
available = false;
super.notify();
return x + y;
//notifyAll();
}
public synchronized void put( int a, int b) {
while ( available == true ) {
try{
wait();
} catch( InterruptedException e) {
System.out.println("Tray Wrong 2");
}
}
//System.out.println("Producer #" + "put:(" + a + "," + b + ").");
available = true;
x = a;
y = b;
super.notify();
//notifyAll();
}
}
public class Test {
public static void main ( String [] args ) {
Tray t = new Tray();
Producer p1 = new Producer(t,1);
Consumer c1 = new Consumer(t,1);
p1.start();
c1.start();
try{
Thread.sleep(10000);
} catch ( InterruptedException e ) {
System.out.println("Thread Interrupted");
}
System.out.println("End");
}
}
做的改动有
1.改变了消费者类的run()方法的循环次数,改成了100次,和生产者的总循环次数保持一致
2.把run()方法里面的进行put()或者get()和输出用synchronized块括起来
分析:
出现bug的原因不是先消费后生产,而是因为print的时候会有延迟,这个延迟多久也是不好控制的,然后这个时候消费者已经可以开始消费了,就导致了消费者的输出先于生产者,所以写同步块的效果就是把输出的时候限定在切换到另一个进程之前。这样子的话,把put和get方法的前面的synchronized关键字去掉也是可以的。