<pre name="code" class="java">一:多线程中生产者,消费者问题
生产者生产产品,消费者消费产品,无产品的时候,消费者无法消费,有产品的时候生产者不需要生产。(始终只有一个产品的情况)
生产消费模型中存在的问题:
1,初始状态下,无产品,消费者不能消费
2,当有产品存在时,生产者不需要生产,等待消费者消费之后再行生产
3,当有产品存在时,消费者可以进行消费,消费之后产品不再存在,需要等待生产者再进行生产
下面的例子程序错误的实行了一个简单生产者/消费者的问题。它由四个类组成:Q,设法获得同步的序列;Producer,产生排队的线程对象;Consumer,消费序列的线程对象;以及PC,创建单个Q,Producer,和Consumer的小类。
<pre name="code" class="java">class Q {
int n;
synchronized int get() {
System.out.println("Got: " + n);
return n;
}
synchronized void put(int n) {
this.n = n;
System.out.println("Put: " + n);
}
}
class Producer implements Runnable {
Q q;
Producer(Q q) {
this.q = q;
new Thread(this, "Producer").start();
}
public void run() {
int i = 0;
while(true) {
q.put(i++);
}
}
}
class Consumer implements Runnable {
Q q;
Consumer(Q q) {
this.q = q;
new Thread(this, "Consumer").start();
}
public void run() {
while(true) {
q.get();
}
}
}
class PC {
public static void main(String args[]) {
Q q = new Q();
new Producer(q);
new Consumer(q);
System.out.println("Press Control-C to stop.");
}
}
虽然在Q类中,put和get方法加了同步锁,但是并不能阻止生产消费错误的产生。如下输出:
Put: 0 count:1
Press Control-C to stop.
Got: 0 count: 1
Put: 1 count:1
Got: 1 count: 1
Got: 1 count: 0
Put: 2 count:0
Put: 3 count:1
Got: 3 count: 1
Put: 4 count:1
Got: 4 count: 1
Put: 5 count:1
Got: 5 count: 1
Put: 6 count:1
Got: 6 count: 1
Put: 7 count:1
Got: 7 count: 1
Got: 7 count: 0
当消费者线程连续执行时,消费者存在两次消费同一个产品的问题。
解决办法:使用信号量semaphore的方式来解决
1,在Q类中设置一个计数器int count = 0;生产者线程生产一个产品count+1,消费者线程消费一个产品count-1,使count在0和1之间变化
package thread;
class Q {
int n=0;
int count = 0;
synchronized int get() {
if(count>0){
System.out.println("Got: " + n+" count: "+count);
count--;
return n;
}else
return -1;
}
synchronized void put(int n) {
if(count<1){
this.n = n;
count++;
System.out.println("Put: " + n+" count:"+count);
}
}
}
class Producer implements Runnable {
Q q;
Producer(Q q) {
this.q = q;
new Thread(this, "Producer").start();
}
public void run() {
int i = 0;
while(true) {
q.put(i++);
try {
Thread.sleep(1*1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
Q q;
Consumer(Q q) {
this.q = q;
new Thread(this, "Consumer").start();
}
public void run() {
while(true) {
q.get();
try {
Thread.sleep(1*1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
class PC {
public static void main(String args[]) {
Q q = new Q();
new Producer(q);
new Consumer(q);
System.out.println("Press Control-C to stop.");
}
}
使用该方法时,生产消费线程一直在执行,只是会产生无输出结果的方法调用。
2,使用wait和notify方法来阻塞、唤醒线程
在Q类中设置boolean product = false信号量,当消费者起初企图消费时,使用wait方法阻塞消费者线程,并唤醒生产者线程进行生产,
当生产线程生产之后,唤醒消费者线程
package thread;
class Q {
int n=0;
boolean product = false;
synchronized int get() {
if(!product)
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
product = false ;
System.out.println("Got: " + n);
notify();
return n;
}
synchronized void put(int n) {
if(product)
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.n = n;
product=true;
notify();
System.out.println("Put: " + n+" count:");
}
}
class Producer implements Runnable {
Q q;
Producer(Q q) {
this.q = q;
new Thread(this, "Producer").start();
}
public void run() {
int i = 0;
while(true) {
q.put(i++);
try {
Thread.sleep(1*1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
Q q;
Consumer(Q q) {
this.q = q;
new Thread(this, "Consumer").start();
}
public void run() {
while(true) {
q.get();
try {
Thread.sleep(1*1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
class PC {
public static void main(String args[]) {
Q q = new Q();
new Producer(q);
new Consumer(q);
System.out.println("Press Control-C to stop.");
}
}
该方法会阻塞、唤醒线程,不同于上一种方法,输出结果:
Put: 0 count:
Press Control-C to stop.
Got: 0
Put: 1 count:
Got: 1
Put: 2 count:
Got: 2
Put: 3 count:
Got: 3
Put: 4 count:
Got: 4
Put: 5 count:
Got: 5
Put: 6 count:
Got: 6
Put: 7 count:
Got: 7