在网上找到一个生产消费线程的例子,是马士兵写的据说,我把代码拷贝出来运行了一下,我们来看看有什么问题,先来看看原来的代码:
package com.darren.test.thread;
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
}
}
/**
* 定义一个WoTou类 ,在类中有id以标记是哪个窝头,重写了toString方法
*/
class WoTou {
private int id;
WoTou(int id) {
this.id = id;
}
@Override
public String toString() {
return "WoTou : " + id;
}
}
/**
* 定义一个篮子的对象,用于装WoTou
*
* push方法,用于装WoTou
*
* pop方法,用于吃WoTou
*/
class SyncStack {
private int index = 0;
private WoTou[] arrWT = new WoTou[6];
/**
* wait()只有在锁定(synchronized)的时候才能使用
*/
// push用于装WoTou,当WoTou数达到6时wait()休息
public synchronized void push(WoTou wt) {
while (index == arrWT.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 唤醒wait()
this.notifyAll();
arrWT[index] = wt;
index++;
}
// pop用于吃WoTou,当WoTou数为0时wait()
public synchronized WoTou pop() {
while (index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return arrWT[index];
}
}
/**
* 实现Runnable接口,实现run方法的Producer类,一个对象只生产20个WoTou
*/
class Producer implements Runnable {
private SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for (int i = 0; i < 20; i++) {
WoTou wt = new WoTou(i);
ss.push(wt);
System.out.println("生产了:" + wt);
try {
Thread.sleep((int) (Math.random() * 200));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 实现Runnable接口,实现run方法的Consumer类,一个对象只消费20个WoTou
*/
class Consumer implements Runnable {
private SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for (int i = 0; i < 20; i++) {
WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果是这样的:
生产了:WoTou : 0
生产了:WoTou : 0
消费了: WoTou : 0
生产了:WoTou : 0
生产了:WoTou : 1
生产了:WoTou : 1
生产了:WoTou : 2
生产了:WoTou : 1
消费了: WoTou : 1
生产了:WoTou : 2
消费了: WoTou : 2
生产了:WoTou : 3
消费了: WoTou : 3
生产了:WoTou : 4
消费了: WoTou : 4
生产了:WoTou : 5
生产了:WoTou : 6
消费了: WoTou : 5
生产了:WoTou : 7
消费了: WoTou : 6
生产了:WoTou : 8
消费了: WoTou : 7
生产了:WoTou : 9
消费了: WoTou : 8
消费了: WoTou : 9
生产了:WoTou : 10
生产了:WoTou : 11
消费了: WoTou : 10
消费了: WoTou : 11
生产了:WoTou : 12
消费了: WoTou : 12
生产了:WoTou : 2
生产了:WoTou : 3
消费了: WoTou : 2
消费了: WoTou : 3
生产了:WoTou : 4
消费了: WoTou : 4
生产了:WoTou : 13
消费了: WoTou : 13
生产了:WoTou : 14
消费了: WoTou : 14
生产了:WoTou : 3
生产了:WoTou : 4
消费了: WoTou : 3
生产了:WoTou : 5
消费了: WoTou : 4
分析一下,有三个生产线程,一个消费线程,最终没有消费完,想必马老师有点懒,id号都一样,也不好看出开,稍微改动一点看看,先把生产线程改为一个,再加点log看看情况,帮助理解:
package com.darren.test.thread;
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
// new Thread(p).start();
// new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
}
}
/**
* 定义一个WoTou类 ,在类中有id以标记是哪个窝头,重写了toString方法
*/
class WoTou {
private int id;
WoTou(int id) {
this.id = id;
}
@Override
public String toString() {
return "WoTou : " + id;
}
}
/**
* 定义一个篮子的对象,用于装WoTou
*
* push方法,用于装WoTou
*
* pop方法,用于吃WoTou
*/
class SyncStack {
private int index = 0;
private WoTou[] arrWT = new WoTou[6];
/**
* wait()只有在锁定(synchronized)的时候才能使用
*/
// push用于装WoTou,当WoTou数达到6时wait()休息
public synchronized void push(WoTou wt) {
while (index == arrWT.length) {
try {
System.out.println("等待消费!");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 唤醒wait()
this.notifyAll();
arrWT[index] = wt;
index++;
}
// pop用于吃WoTou,当WoTou数为0时wait()
public synchronized WoTou pop() {
while (index == 0) {
try {
System.out.println("等待生产!");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return arrWT[index];
}
}
/**
* 实现Runnable接口,实现run方法的Producer类,一个对象只生产20个WoTou
*/
class Producer implements Runnable {
private SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for (int i = 0; i < 20; i++) {
WoTou wt = new WoTou(i);
ss.push(wt);
System.out.println("生产了:" + wt);
try {
Thread.sleep((int) (Math.random() * 200));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 实现Runnable接口,实现run方法的Consumer类,一个对象只消费20个WoTou
*/
class Consumer implements Runnable {
private SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for (int i = 0; i < 20; i++) {
WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费了: WoTou : 0
生产了:WoTou : 0
生产了:WoTou : 1
生产了:WoTou : 2
消费了: WoTou : 2
生产了:WoTou : 3
生产了:WoTou : 4
消费了: WoTou : 4
生产了:WoTou : 5
消费了: WoTou : 5
生产了:WoTou : 6
生产了:WoTou : 7
生产了:WoTou : 8
生产了:WoTou : 9
等待消费!
生产了:WoTou : 10
消费了: WoTou : 9
等待消费!
消费了: WoTou : 10
生产了:WoTou : 11
等待消费!
消费了: WoTou : 11
生产了:WoTou : 12
等待消费!
消费了: WoTou : 12
生产了:WoTou : 13
等待消费!
消费了: WoTou : 13
生产了:WoTou : 14
等待消费!
消费了: WoTou : 14
生产了:WoTou : 15
等待消费!
消费了: WoTou : 15
生产了:WoTou : 16
等待消费!
生产了:WoTou : 17
消费了: WoTou : 16
等待消费!
消费了: WoTou : 17
生产了:WoTou : 18
等待消费!
消费了: WoTou : 18
生产了:WoTou : 19
消费了: WoTou : 19
消费了: WoTou : 8
消费了: WoTou : 7
消费了: WoTou : 6
消费了: WoTou : 3
消费了: WoTou : 1
这下是不是清楚多了,最后也全部消费完了,还能看到有等待的情况,这种情况看明白以后,我们再改动点,去看明白马老师写的代码:
package com.darren.test.thread;
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p1 = new Producer(ss, 1);
Producer p2 = new Producer(ss, 2);
Producer p3 = new Producer(ss, 3);
Consumer c = new Consumer(ss);
new Thread(p1).start();
new Thread(p2).start();
new Thread(p3).start();
new Thread(c).start();
}
}
/**
* 定义一个WoTou类 ,在类中有id以标记是哪个窝头,重写了toString方法
*/
class WoTou {
/**
* 窝头ID
*/
private int id;
/**
* 生产者ID
*/
private int producerId;
WoTou(int id, int producerId) {
this.id = id;
this.producerId = producerId;
}
@Override
public String toString() {
return "WoTou [窝头ID=" + id + ", 生产者ID=" + producerId + "]";
}
}
/**
* 定义一个篮子的对象,用于装WoTou
*
* push方法,用于装WoTou
*
* pop方法,用于吃WoTou
*/
class SyncStack {
private int index = 0;
private WoTou[] arrWT = new WoTou[6];
/**
* wait()只有在锁定(synchronized)的时候才能使用
*/
// push用于装WoTou,当WoTou数达到6时wait()休息
public synchronized void push(WoTou wt) {
while (index == arrWT.length) {
try {
System.out.println("等待消费!");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 唤醒wait()
this.notifyAll();
arrWT[index] = wt;
index++;
}
// pop用于吃WoTou,当WoTou数为0时wait()
public synchronized WoTou pop() {
while (index == 0) {
try {
System.out.println("等待生产!");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return arrWT[index];
}
}
/**
* 实现Runnable接口,实现run方法的Producer类,一个对象只生产20个WoTou
*/
class Producer implements Runnable {
/**
* 生产者ID
*/
private int id;
private SyncStack ss = null;
Producer(SyncStack ss, int id) {
this.ss = ss;
this.id = id;
}
public void run() {
for (int i = 0; i < 20; i++) {
WoTou wt = new WoTou(i, id);
ss.push(wt);
System.out.println("生产了:" + wt);
try {
Thread.sleep((int) (Math.random() * 200));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 实现Runnable接口,实现run方法的Consumer类,一个对象只消费20个WoTou
*/
class Consumer implements Runnable {
private SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for (int i = 0; i < 20; i++) {
WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
生产了:WoTou [窝头ID=0, 生产者ID=1]
生产了:WoTou [窝头ID=0, 生产者ID=3]
生产了:WoTou [窝头ID=0, 生产者ID=2]
消费了: WoTou [窝头ID=0, 生产者ID=3]
生产了:WoTou [窝头ID=1, 生产者ID=2]
生产了:WoTou [窝头ID=1, 生产者ID=1]
生产了:WoTou [窝头ID=2, 生产者ID=1]
生产了:WoTou [窝头ID=1, 生产者ID=3]
等待消费!
等待消费!
等待消费!
等待消费!
等待消费!
生产了:WoTou [窝头ID=2, 生产者ID=2]
消费了: WoTou [窝头ID=1, 生产者ID=3]
等待消费!
消费了: WoTou [窝头ID=2, 生产者ID=2]
生产了:WoTou [窝头ID=3, 生产者ID=2]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=3, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=4, 生产者ID=2]
等待消费!
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=4, 生产者ID=2]
生产了:WoTou [窝头ID=5, 生产者ID=2]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=5, 生产者ID=2]
生产了:WoTou [窝头ID=6, 生产者ID=2]
等待消费!
消费了: WoTou [窝头ID=6, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=7, 生产者ID=2]
等待消费!
等待消费!
等待消费!
等待消费!
生产了:WoTou [窝头ID=8, 生产者ID=2]
消费了: WoTou [窝头ID=7, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=3, 生产者ID=1]
消费了: WoTou [窝头ID=8, 生产者ID=2]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=3, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=4, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=4, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=5, 生产者ID=1]
等待消费!
等待消费!
消费了: WoTou [窝头ID=5, 生产者ID=1]
生产了:WoTou [窝头ID=6, 生产者ID=1]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=6, 生产者ID=1]
生产了:WoTou [窝头ID=7, 生产者ID=1]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=7, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=8, 生产者ID=1]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=8, 生产者ID=1]
生产了:WoTou [窝头ID=9, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=10, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=9, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=11, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=10, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=12, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=11, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=12, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=13, 生产者ID=1]
等待消费!
等待消费!
消费了: WoTou [窝头ID=13, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=14, 生产者ID=1]
等待消费!
很清楚的看出消费者消费了不同生产者生产的窝头,想必这才是马老师想要表达的意思
仔细看看,这个结果好像还不够热闹,不如再加一个消费线程看看:
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p1 = new Producer(ss, 1);
Producer p2 = new Producer(ss, 2);
Producer p3 = new Producer(ss, 3);
Consumer c = new Consumer(ss);
new Thread(p1).start();
new Thread(p2).start();
new Thread(p3).start();
new Thread(c).start();
new Thread(c).start();
}
生产了:WoTou [窝头ID=0, 生产者ID=1]
消费了: WoTou [窝头ID=0, 生产者ID=3]
生产了:WoTou [窝头ID=0, 生产者ID=3]
生产了:WoTou [窝头ID=0, 生产者ID=2]
消费了: WoTou [窝头ID=0, 生产者ID=2]
生产了:WoTou [窝头ID=1, 生产者ID=1]
生产了:WoTou [窝头ID=1, 生产者ID=3]
生产了:WoTou [窝头ID=2, 生产者ID=1]
生产了:WoTou [窝头ID=1, 生产者ID=2]
生产了:WoTou [窝头ID=3, 生产者ID=1]
等待消费!
等待消费!
消费了: WoTou [窝头ID=3, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=4, 生产者ID=1]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=4, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=5, 生产者ID=1]
等待消费!
等待消费!
消费了: WoTou [窝头ID=5, 生产者ID=1]
生产了:WoTou [窝头ID=6, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=6, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=2, 生产者ID=2]
生产了:WoTou [窝头ID=2, 生产者ID=3]
消费了: WoTou [窝头ID=2, 生产者ID=2]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=2, 生产者ID=3]
等待消费!
等待消费!
生产了:WoTou [窝头ID=7, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=7, 生产者ID=1]
生产了:WoTou [窝头ID=3, 生产者ID=2]
等待消费!
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=3, 生产者ID=2]
生产了:WoTou [窝头ID=4, 生产者ID=2]
等待消费!
等待消费!
消费了: WoTou [窝头ID=4, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=5, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=6, 生产者ID=2]
消费了: WoTou [窝头ID=5, 生产者ID=2]
等待消费!
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=6, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=7, 生产者ID=2]
等待消费!
等待消费!
消费了: WoTou [窝头ID=7, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=8, 生产者ID=2]
生产了:WoTou [窝头ID=3, 生产者ID=3]
等待消费!
消费了: WoTou [窝头ID=8, 生产者ID=2]
等待消费!
等待消费!
生产了:WoTou [窝头ID=9, 生产者ID=2]
消费了: WoTou [窝头ID=3, 生产者ID=3]
消费了: WoTou [窝头ID=1, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=4, 生产者ID=3]
等待消费!
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=9, 生产者ID=2]
生产了:WoTou [窝头ID=10, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=11, 生产者ID=2]
消费了: WoTou [窝头ID=10, 生产者ID=2]
等待消费!
等待消费!
消费了: WoTou [窝头ID=11, 生产者ID=2]
生产了:WoTou [窝头ID=5, 生产者ID=3]
等待消费!
等待消费!
等待消费!
生产了:WoTou [窝头ID=12, 生产者ID=2]
消费了: WoTou [窝头ID=5, 生产者ID=3]
等待消费!
等待消费!
等待消费!
生产了:WoTou [窝头ID=8, 生产者ID=1]
消费了: WoTou [窝头ID=12, 生产者ID=2]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=8, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=13, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=14, 生产者ID=2]
消费了: WoTou [窝头ID=13, 生产者ID=2]
等待消费!
等待消费!
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=14, 生产者ID=2]
生产了:WoTou [窝头ID=15, 生产者ID=2]
等待消费!
等待消费!
消费了: WoTou [窝头ID=15, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=16, 生产者ID=2]
消费了: WoTou [窝头ID=16, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=9, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=17, 生产者ID=2]
消费了: WoTou [窝头ID=9, 生产者ID=1]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=17, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=10, 生产者ID=1]
等待消费!
等待消费!
消费了: WoTou [窝头ID=10, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=11, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=11, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=12, 生产者ID=1]
等待消费!
等待消费!
消费了: WoTou [窝头ID=12, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=13, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=13, 生产者ID=1]
生产了:WoTou [窝头ID=18, 生产者ID=2]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=18, 生产者ID=2]
等待消费!
生产了:WoTou [窝头ID=19, 生产者ID=2]
生产了:WoTou [窝头ID=6, 生产者ID=3]
等待消费!
消费了: WoTou [窝头ID=19, 生产者ID=2]
消费了: WoTou [窝头ID=6, 生产者ID=3]
生产了:WoTou [窝头ID=14, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=15, 生产者ID=1]
消费了: WoTou [窝头ID=14, 生产者ID=1]
等待消费!
等待消费!
生产了:WoTou [窝头ID=16, 生产者ID=1]
等待消费!
消费了: WoTou [窝头ID=15, 生产者ID=1]
等待消费!
生产了:WoTou [窝头ID=17, 生产者ID=1]
消费了: WoTou [窝头ID=16, 生产者ID=1]
等待消费!
等待消费!
等待消费!
消费了: WoTou [窝头ID=17, 生产者ID=1]
生产了:WoTou [窝头ID=18, 生产者ID=1]
等待消费!
这下热闹了吧,最后一段就当娱乐吧。