2.解决进度25%:只是将Clerk中两个方法添加上了Sychronized
3.解决进度50%:添加线程通信,生产者生产满了等待;消费者消费完了,等待。
4.解决进度75%:生产者如果添加200ms的延迟,会导致,消费进程无法停止。
一、生产者和消费者问题
针对同一个对象,消费者消费和生产者生产,是同时进行。这样会导致多线程访问问题,需要使用线程同步,解决这个问题。如下进行分步分析,从问题开始,到使用sychronized或者lock关键字,解决生产者和消费者问题。
二、问题产生和解决
1.不考虑线程同步导致的问题
(1)查看类图
(2)Clerk:共用类
package thread.thread.ProAndCon;
/**
* 店员
*/
class Clerk{
private int product=0;
//进货
public void get(){
if(product>=10){
System.out.println("产品已满!");
// try {
// this.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}else{
System.out.println(Thread.currentThread().getName()+":"+ ++product);
// this.notifyAll();
}
}
//卖货
public void sale(){
if(product<=0){
System.out.println("产品已没有了!");
// try {
// this.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}else {
System.out.println(Thread.currentThread().getName()+":"+ --product);
// this.notifyAll();
}
}
}
(3)生产者:Producer
实现功能:生产20个产品
package thread.thread.ProAndCon;
/**
* 生产者
*/
class Producer implements Runnable{
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i <= 20; i++) {
clerk.get();
}
}
}
(4)消费者:Consumer
实现功能:消费20个产品
package thread.thread.ProAndCon;
/**
* 消费者
*/
class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i <= 20; i++) {
clerk.sale();
}
}
}
(5)测试类:TestProductorAndConsumer
package thread.thread.ProAndCon;
/**
* Created by Administrator on 2020/1/16.
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Producer producer=new Producer(clerk);
Consumer consumer=new Consumer(clerk);
new Thread(producer,"生产者A").start();
new Thread(consumer,"消费者B").start();
}
}
2.解决进度25%:只是将Clerk中两个方法添加上了Sychronized
(1)店员:Clerk
package thread.thread.ProAndCon_Sychronized;
/**
* 店员
*/
class Clerk{
private int product=0;
//进货
public synchronized void get(){
if(product>=10){
System.out.println("产品已满!");
// try {
// this.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}else{
System.out.println(Thread.currentThread().getName()+":"+ ++product);
// this.notifyAll();
}
}
//卖货
public synchronized void sale(){
if(product<=0){
System.out.println("产品已没有了!");
// try {
// this.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}else {
System.out.println(Thread.currentThread().getName()+":"+ --product);
// this.notifyAll();
}
}
}
(2)运行测试类会导致
/**
* Created by Administrator on 2020/1/16.
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Producer producer=new Producer(clerk);
Consumer consumer=new Consumer(clerk);
new Thread(producer,"生产者A").start();
new Thread(consumer,"消费者B").start();
}
问题:生产者生产满了10个,但是持续生产;消费者消费到0个,继续消费。
生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
生产者A:10
产品已满!
产品已满!
产品已满!
产品已满!
产品已满!
产品已满!
产品已满!
消费者B:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
生产者A:1
生产者A:2
生产者A:3
生产者A:4
3.解决进度50%:添加线程通信,生产者生产满了等待;消费者消费完了,等待。
(1)店员:Clerk
package thread.thread.ProAndCon_Sychronized;
/**
* 店员
*/
class Clerk{
private int product=0;
//进货
public synchronized void get(){
if(product>=10){
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println(Thread.currentThread().getName()+":"+ ++product);
this.notifyAll();
}
}
//卖货
public synchronized void sale(){
if(product<=0){
System.out.println("产品已没有了!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+":"+ --product);
this.notifyAll();
}
}
}
(2)运行测试类结果
生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
生产者A:10
产品已满!
消费者B:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0
产品已没有了!
生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
生产者A:10
消费者B:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0
4.解决进度75%:生产者如果添加200ms的延迟,会导致,消费进程无法停止。
(1)生产者添加200ms延迟(模拟生产环境)
package thread.thread.ProAndCon_Sychronized;
/**
* 生产者
*/
class Producer implements Runnable{
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i <= 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
(2)店员:Clerk修改生产者上限为1
package thread.thread.ProAndCon_Sychronized;
/**
* 店员
*/
class Clerk{
private int product=0;
//进货
public synchronized void get(){
if(product>=1){
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println(Thread.currentThread().getName()+":"+ ++product);
this.notifyAll();
}
}
//卖货
public synchronized void sale(){
if(product<=0){
System.out.println("产品已没有了!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+":"+ --product);
this.notifyAll();
}
}
}
(3)运行测试代码:发现进程无法停止
(4)原因分析
情景:当消费者剩下1次,生产者剩下2次。
消费者消费最后一次,product=0,等待wait;
生产者生产倒数第2次,product=1,并且通知消费者,消费者完成最后一次进程,但是此时product=1;
生产者进行最后一次生产,product进入wait,而没有其他人唤醒,所以程序无法结束。
(5)解决问题:修改Clerk,把生产和消费的else去掉
package thread.thread.ProAndCon_SychronizedImp;
/**
* 店员
*/
class Clerk{
private int product=0;
//进货
public synchronized void get(){
if(product>=1){
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// else{
// System.out.println(Thread.currentThread().getName()+":"+ ++product);
// this.notifyAll();
// }
System.out.println(Thread.currentThread().getName()+":"+ ++product);
this.notifyAll();
}
//卖货
public synchronized void sale(){
if(product<=0){
System.out.println("产品已没有了!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// else {
// System.out.println(Thread.currentThread().getName()+":"+ --product);
// this.notifyAll();
// }
System.out.println(Thread.currentThread().getName()+":"+ --product);
this.notifyAll();
}
}
测试后结果:顺利结束
5.解决进度100%:多个生产者和消费者导致问题优化
(1)测试添加C生产者和D消费者
package thread.thread.ProAndCon_SychronizedImp_muti;
/**
* Created by Administrator on 2020/1/16.
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Producer producer=new Producer(clerk);
Consumer consumer=new Consumer(clerk);
new Thread(producer,"生产者A").start();
new Thread(consumer,"消费者B").start();
new Thread(producer,"生产者C").start();
new Thread(consumer,"消费者D").start();
}
}
结果:
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
消费者B:-1
产品已没有了!
消费者D:-2
产品已没有了!
消费者B:-3
产品已没有了!
消费者D:-4
产品已没有了!
消费者B:-5
产品已没有了!
消费者D:-6
产品已没有了!
消费者B:-7
产品已没有了!
消费者D:-8
产品已没有了!
消费者B:-9
产品已没有了!
消费者D:-10
产品已没有了!
消费者B:-11
产品已没有了!
生产者C:-10
消费者B:-11
产品已没有了!
消费者D:-12
产品已没有了!
消费者B:-13
产品已没有了!
消费者D:-14
产品已没有了!
消费者B:-15
产品已没有了!
消费者D:-16
产品已没有了!
消费者B:-17
产品已没有了!
消费者D:-18
产品已没有了!
消费者B:-19
产品已没有了!
消费者D:-20
产品已没有了!
消费者B:-21
产品已没有了!
消费者D:-22
产品已没有了!
消费者B:-23
产品已没有了!
消费者D:-24
产品已没有了!
消费者B:-25
产品已没有了!
消费者D:-26
产品已没有了!
消费者B:-27
产品已没有了!
消费者D:-28
产品已没有了!
消费者B:-29
产品已没有了!
消费者D:-30
产品已没有了!
消费者B:-31
产品已没有了!
消费者D:-32
产品已没有了!
消费者B:-33
产品已没有了!
消费者D:-34
产品已没有了!
消费者B:-35
产品已没有了!
消费者D:-36
产品已没有了!
消费者B:-37
产品已没有了!
消费者D:-38
产品已没有了!
消费者B:-39
消费者D:-40
生产者A:-39
生产者C:-38
生产者A:-37
生产者C:-36
生产者A:-35
生产者C:-34
生产者A:-33
生产者C:-32
生产者A:-31
生产者C:-30
生产者A:-29
生产者C:-28
生产者A:-27
生产者C:-26
生产者A:-25
生产者C:-24
生产者A:-23
生产者C:-22
生产者A:-21
生产者C:-20
生产者A:-19
生产者C:-18
生产者A:-17
生产者C:-16
生产者A:-15
生产者C:-14
生产者A:-13
生产者C:-12
生产者A:-11
生产者C:-10
生产者A:-9
生产者C:-8
生产者A:-7
生产者C:-6
生产者A:-5
生产者C:-4
生产者A:-3
生产者C:-2
生产者A:-1
生产者C:0
Process finished with exit code 0
(2)导致问题:虚假唤醒
(3)解决:修改Clerk类的生产和消费方法的if判断语句,为while循环判断
package thread.thread.ProAndCon_SychronizedImp_muti;
/**
* 店员
*/
class Clerk{
private int product=0;
//进货
public synchronized void get(){
while(product>=1){
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// else{
// System.out.println(Thread.currentThread().getName()+":"+ ++product);
// this.notifyAll();
// }
System.out.println(Thread.currentThread().getName()+":"+ ++product);
this.notifyAll();
}
//卖货
public synchronized void sale(){
while (product<=0){
System.out.println("产品已没有了!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// else {
// System.out.println(Thread.currentThread().getName()+":"+ --product);
// this.notifyAll();
// }
System.out.println(Thread.currentThread().getName()+":"+ --product);
this.notifyAll();
}
}
重新测试结果:
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
生产者A:1
消费者B:0