说明:
有三个生产者,三个消费者(各三个线程代表)。当产品数为0时开始生产产品,生产一个后通知消费者消耗产品,之后又生产。所以一直在0和1中徘徊,正确输出为1和0交替出现。
一.if 控制等待条件,代码和结果如下
执行代码:Test
package com.hz.wjsc.mservice.syntheticwarfare.test.thread.produce;
public class Test {
public static void main(String[] args) {
Product p = new Product();
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
p.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "生产者" + (i + 1)).start();
}
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
p.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "消费者" + (i + 1)).start();
}
}
}
产品代码:Product
package com.hz.wjsc.mservice.syntheticwarfare.test.thread.produce;
public class Product {
private int number = 0;
public synchronized void increment () throws InterruptedException {
Thread.sleep(2000);
if(number != 0) {
System.out.println(Thread.currentThread().getName() +":要等待了,产品数:"+number);
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + ":生产完成,产品数:" + number);
this.notifyAll();
}
public synchronized void decrement () throws InterruptedException {
if(number < 1) {
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() +":要等待了,产品数:"+number);
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + ":消费完成,产品数:" + number);
this.notifyAll();
}
}
结果如下:
生产者1:生产完成,产品数:1
消费者3:消费完成,产品数:0
消费者2:要等待了,产品数:0
消费者1:要等待了,产品数:0
生产者2:生产完成,产品数:1
生产者3:要等待了,产品数:1
消费者1:消费完成,产品数:0
消费者2:消费完成,产品数:-1
生产者3:生产完成,产品数:0
结果:出现了-1,和预想不符合
分析:
- 对于该类中的同步方法,一旦哪个线程进入执行别的线程就不能进入只能等待,除非别的方法执行完或者进入等待wait中,执行完和进入等待状态都会释放锁,而sleep不会释放锁
- 进入等待的线程是不能参与线程间的竞争,唤醒后可以和别的线程公平竞争
- 生产者1抢占进入方法,完成生产,线程结束。产品数为1(生产者1进入方法后别的线程不能进入)
- 除生产者1外的5个线程公平竞争,消费者3一往无前抢占进入方法完成消费,线程结束。产品数为0
- 除生产者1、消费者3外的4个线程公平竞争,消费者2抢到了,但是产品数为0,进入if中等待。产品数为0
- 除生产者1、消费者3、消费者2外的3个线程公平竞争,消费者1抢到了,但是产品数为0,进入if中等待。产品数为0
- 除生产者1、消费者3、消费者2、消费者1外的2个线程公平竞争,生产者2抢到了完成生产,并唤醒了所有等待中的线程,线程结束。产品数为1
- 除生产者1、消费者3、生产者2外的3个线程公平竞争,生产者3抢到了,但是产品数为1,进入if中等待。产品数为1(消费者2、消费者1被叫醒了,又来抢了,抢的一身劲)
- 除生产者1、消费者3、生产者2、生产者3外的2个线程公平竞争,消费者1抢到了,完成消费,并唤醒了所有等待中的线程,线程结束。产品数为0
- 除生产者1、消费者3、生产者2、消费者1外的2个线程公平竞争,消费者2抢到了,此时虽然产品数为0,但消费者2是从if中醒来的,不会在判断进入if等待,直接向下执行,完成消费,并唤醒了所有等待中的线程,线程结束。产品数为-1
- 除生产者1、消费者3、生产者2、消费者1、消费者2外的1个线程公平竞争,生产者3不用抢了,完成生产,线程结束。产品数为0
出现问题:
消费者2、消费者1先后进入等待,又先后唤醒执行,唤醒后不再进行if判断,也就和产品数无关了,直接消费出现问题
二.while控制等待条件
产品代码:Product
package com.hz.wjsc.mservice.syntheticwarfare.test.thread.produce;
public class Product {
private int number = 0;
public synchronized void increment () throws InterruptedException {
Thread.sleep(2000);
while(number != 0) {
System.out.println(Thread.currentThread().getName() +":要等待了,产品数:"+number);
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + ":生产完成,产品数:" + number);
this.notifyAll();
}
public synchronized void decrement () throws InterruptedException {
while(number < 1) {
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() +":要等待了,产品数:"+number);
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + ":消费完成,产品数:" + number);
this.notifyAll();
}
}
结果如下:
生产者1:生产完成,产品数:1
消费者2:消费完成,产品数:0
消费者3:要等待了,产品数:0
消费者1:要等待了,产品数:0
生产者3:生产完成,产品数:1
生产者2:要等待了,产品数:1
消费者1:消费完成,产品数:0
消费者3:要等待了,产品数:0
生产者2:生产完成,产品数:1
消费者3:消费完成,产品数:0
分析:去除等待打印(3、4、6、8行)比上面(3、4、6行)多了一行,说明while比if多判断了一次并进入等待状态,就是1、0交替出现,没有问题
消费者3、消费者1先后进入if中等待,又先后被唤醒执行,消费者1执行完了后,产品数为0,此时消费者3又抢占开始执行方法,从wait处继续执行,但是while循环是无限循环,还是没有出循环体又判断了一波,产品数为0,又进入等待。关键是if只在进入方法的时候判断一次,而while不论进入方法还是唤醒后继续执行都会再判断一遍从而保证了正确性。
小伙伴们,不到之处还请斧正!