生产者和消费者问题

概述

  • 生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。所谓生产消费者问题,实际上主要是包含了两类线程:
    • 一类是生产者线程用于生产数据
    • 一类是消费者线程用于消费数据
  • 为了耦合生产者和消费者的关系,通常会采用共享的数据区域,就像一个仓库。生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为,消费者只需要从共享数据区中获取数据,并不需要关心生产者的行为。

使用Sychronized实现

  1. 为了体现生产和消费过程总的等待和唤醒,Java就提供了几个方法供我们使用,这几个方法就在Object类中Object类的等待和唤醒方法(隐式锁)

  2. viod wait( ):导致当前线程等待,直到另一个线程调用该对象的notify()方法和notifyAll()方法

  3. void notify( ) : 唤醒正在等待对象监视器的单个线程

  4. void notifyAll( ) : 唤醒正在等待对象监视器的所有线程(注意:wait、notify、notifyAll方法必须要在同步块或同步方法里且成对出现使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package tech.chen.juccode.a02;
/**
* @Date 2022/8/19 19:53
* @Author c-z-k
*/
public class producersAndConsumers {
public static void main(String[] args) {
AirCondition airCondition=new AirCondition();
new Thread(()->{ for (int i = 1; i <11 ; i++) airCondition.increment();},"线程A").start();
new Thread(()->{ for (int i = 1; i <11 ; i++) airCondition.decrement();},"线程B").start();
new Thread(()->{ for (int i = 1; i <11 ; i++) airCondition.increment();},"线程C").start();
new Thread(()->{ for (int i = 1; i <11 ; i++) airCondition.decrement();},"线程D").start();
}
static class AirCondition{
private int number=0;

public synchronized void increment(){
//1.判断
/* if(number!=0){*/
while(number!=0){
try {
//为什么不用if?解释如下
//第一次A进来了,在number++后(number=1) C抢到执行权,进入wait状态
//这个时候,A抢到cpu执行权,也进入wait状态,此时,B线程进行了一次消费
//唤醒了线程,这个时候A抢到CPU执行权,不需要做判断,number++(1),唤醒线程
//C也抢到CPU执行权,不需要做判断,number++(2)

this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.干活
number++;
System.out.println(Thread.currentThread().getName()+":"+number);
//3.唤醒
this.notifyAll();
}
public synchronized void decrement(){
/*if (number==0){*/
while (number==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(Thread.currentThread().getName()+":"+number);
this.notifyAll();
}
}
}

使用ReentrantLock实现 (显示锁)

  • ReentrantLock( ):创建一个ReentrantLock的实例
  • void lock( ):获得锁
  • void unlock( ):释放锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* @Date 2022/8/19 20.03
* @Author c-z-k
*/
@SuppressWarnings("all")
public class producersAndConsumers2 {
public static void main(String[] args) {
AirCondition airCondition=new AirCondition();
new Thread(()->{ for (int i = 0; i <10 ; i++) airCondition.decrement();},"线程A").start();
new Thread(()->{ for (int i = 0; i <10 ; i++) airCondition.increment();},"线程B").start();
new Thread(()->{ for (int i = 0; i <10 ; i++) airCondition.decrement();},"线程C").start();
new Thread(()->{ for (int i = 0; i <10 ; i++) airCondition.increment();},"线程D").start();
}
}
class AirCondition{
private int number=0;
//定义Lock锁对象
final Lock lock=new ReentrantLock();
final Condition condition = lock.newCondition();

//生产者,如果number=0就 number++
public void increment(){
lock.lock();
try {
//1.判断
while(number!=0){
try {
condition.await();//this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.干活
number++;
System.out.println(Thread.currentThread().getName()+":\t"+number);
//3.唤醒
condition.signalAll();//this.notifyAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
//消费者,如果number=1,就 number--
public void decrement(){
lock.lock();
try {
//1.判断
while(number==0){
try {
condition.await();//this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.干活
number--;
System.out.println(Thread.currentThread().getName()+":\t"+number);
//3.唤醒
condition.signalAll();//this.notifyAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}