JUC:4_1并发协作模型:生产者消费者模型
线程通信问题
线程之间的通信问题,就是生产者和消费者问题,也就是如何做到多个线程交替执行。
并发协作模型:生产者消费者模型 解决方式1.管程法
并发协作模型:生产者消费者模型 解决方式2.信号灯法
上面两个是基于synchronized、wait、notify来去做的,现在改为Lock去做。
synchronized版本的管程法
/**
* 并发协作模型"生产者/消费者模式"
* 线程之间的通信问题,就是生产者和消费者问题,也就是如何做到多个线程交替执行
* 比如一个线程++A
* 一个线程--A
* 需要先放入再执行另一个操作
*/
public class TestPC {
public static void main(String[] args) {
SyncContainer container = new SyncContainer();
//生产者
new Thread(() -> {
for (int i = 0; i < 100; i++) {
container.push(new Product(i));
System.out.println("将产品放入缓冲区:" + i);
}
}).start();
//消费者
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("消费产品" + container.pop().getPid());
}
}).start();
}
}
//产品
class Product {
private int pid;//产品编号
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public Product(int pid) {
this.pid = pid;
}
}
//缓冲区
class SyncContainer {
//容器大小,默认容量指定10
private static final int DEFAULT_CAPACITY = 10;
Product[] products = new Product[DEFAULT_CAPACITY];
//容器计数器
int count = 0;
//生产者放产品
public synchronized void push(Product product) {
if (count == products.length) {
//容器已满,需要等待消费
//通知消费者消费,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//容器不满,可以放产品
products[count] = product;
count++;
//通知消费者消费,这里用notifyAll和notify结果都是一样的,因为不会同时阻塞
this.notifyAll();
}
//消费者消费产品
public synchronized Product pop() {
//判断容器有无产品,无则阻塞
if (count == 0) {
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//可以消费,这里主要:count--必须在前,因为生产者生产之后count++会+1
count--;
Product product = products[count];
//通知生产者生产,这里用notifyAll和notify结果都是一样的,因为不会同时阻塞
this.notify();
return product;
}
}
输出:
将产品放入缓冲区:0
将产品放入缓冲区:1
将产品放入缓冲区:2
将产品放入缓冲区:3
将产品放入缓冲区:4
将产品放入缓冲区:5
将产品放入缓冲区:6
将产品放入缓冲区:7
将产品放入缓冲区:8
将产品放入缓冲区:9
消费产品9
将产品放入缓冲区:10
消费产品10
将产品放入缓冲区:11
将产品放入缓冲区:12
消费产品11
消费产品12
将产品放入缓冲区:13
消费产品13
将产品放入缓冲区:14
消费产品14
将产品放入缓冲区:15
消费产品15
将产品放入缓冲区:16
将产品放入缓冲区:17
消费产品16
消费产品17
将产品放入缓冲区:18
将产品放入缓冲区:19
消费产品18
消费产品19
将产品放入缓冲区:20
将产品放入缓冲区:21
消费产品20
消费产品21
将产品放入缓冲区:22
消费产品22
将产品放入缓冲区:23
消费产品23
消费产品24
将产品放入缓冲区:24
消费产品8
将产品放入缓冲区:25
消费产品25
将产品放入缓冲区:26
消费产品26
将产品放入缓冲区:27
将产品放入缓冲区:28
消费产品27
将产品放入缓冲区:29
消费产品29
将产品放入缓冲区:30
消费产品30
将产品放入缓冲区:31
消费产品31
将产品放入缓冲区:32
消费产品32
将产品放入缓冲区:33
消费产品33
将产品放入缓冲区:34
消费产品34
将产品放入缓冲区:35
消费产品35
将产品放入缓冲区:36
消费产品36
将产品放入缓冲区:37
消费产品37
将产品放入缓冲区:38
消费产品38
将产品放入缓冲区:39
消费产品39
将产品放入缓冲区:40
消费产品40
将产品放入缓冲区:41
消费产品41
将产品放入缓冲区:42
消费产品42
将产品放入缓冲区:43
消费产品43
将产品放入缓冲区:44
消费产品44
将产品放入缓冲区:45
消费产品45
将产品放入缓冲区:46
消费产品46
将产品放入缓冲区:47
消费产品47
将产品放入缓冲区:48
消费产品48
将产品放入缓冲区:49
消费产品49
将产品放入缓冲区:50
消费产品50
将产品放入缓冲区:51
消费产品51
将产品放入缓冲区:52
消费产品52
将产品放入缓冲区:53
消费产品53
将产品放入缓冲区:54
消费产品54
将产品放入缓冲区:55
消费产品55
将产品放入缓冲区:56
消费产品56
将产品放入缓冲区:57
消费产品57
将产品放入缓冲区:58
消费产品58
将产品放入缓冲区:59
消费产品59
将产品放入缓冲区:60
消费产品60
将产品放入缓冲区:61
消费产品61
将产品放入缓冲区:62
消费产品62
将产品放入缓冲区:63
将产品放入缓冲区:64
消费产品63
消费产品64
消费产品65
将产品放入缓冲区:65
消费产品28
将产品放入缓冲区:66
消费产品66
将产品放入缓冲区:67
消费产品67
将产品放入缓冲区:68
消费产品68
将产品放入缓冲区:69
消费产品69
将产品放入缓冲区:70
消费产品70
将产品放入缓冲区:71
消费产品71
将产品放入缓冲区:72
消费产品72
将产品放入缓冲区:73
消费产品73
将产品放入缓冲区:74
消费产品74
将产品放入缓冲区:75
将产品放入缓冲区:76
消费产品75
将产品放入缓冲区:77
消费产品77
将产品放入缓冲区:78
将产品放入缓冲区:79
消费产品78
消费产品79
将产品放入缓冲区:80
将产品放入缓冲区:81
消费产品80
消费产品81
将产品放入缓冲区:82
消费产品82
将产品放入缓冲区:83
消费产品83
将产品放入缓冲区:84
消费产品84
将产品放入缓冲区:85
将产品放入缓冲区:86
消费产品85
消费产品86
将产品放入缓冲区:87
消费产品87
将产品放入缓冲区:88
消费产品88
将产品放入缓冲区:89
消费产品89
消费产品90
将产品放入缓冲区:90
消费产品76
将产品放入缓冲区:91
消费产品91
将产品放入缓冲区:92
消费产品92
将产品放入缓冲区:93
消费产品93
将产品放入缓冲区:94
消费产品94
将产品放入缓冲区:95
消费产品95
将产品放入缓冲区:96
消费产品96
将产品放入缓冲区:97
消费产品97
将产品放入缓冲区:98
消费产品98
将产品放入缓冲区:99
消费产品99
消费产品7
消费产品6
消费产品5
消费产品4
消费产品3
消费产品2
消费产品1
消费产品0
Lock版本的管程法
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 并发协作模型"生产者/消费者模式"
* 线程之间的通信问题,就是生产者和消费者问题,也就是如何做到多个线程交替执行
* 比如一个线程++A
* 一个线程--A
* 需要先放入再执行另一个操作
*/
public class TestPC2 {
public static void main(String[] args) {
SyncContainer container = new SyncContainer();
//生产者
new Thread(() -> {
for (int i = 0; i < 100; i++) {
container.push(new Product(i));
System.out.println("将产品放入缓冲区:" + i);
}
}).start();
//消费者
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("消费产品:" + container.pop().getPid());
}
}).start();
}
}
//产品
class Product2 {
private int pid;//产品编号
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public Product2(int pid) {
this.pid = pid;
}
}
//缓冲区
class SyncContainer2 {
//容器大小,默认容量指定10
private static final int DEFAULT_CAPACITY = 10;
Product2[] products = new Product2[DEFAULT_CAPACITY];
final Lock lock = new ReentrantLock();//默认非公平锁,可以添加true类型设置为公平锁
final Condition productor = lock.newCondition();
final Condition consumer = lock.newCondition();
//容器计数器
int count = 0;
//生产者放产品
public void push(Product2 product) {
lock.lock();//加锁
//lock.tryLock();//尝试获取锁,这个就是和synchronized区别,不会傻傻的一直等待
try {
if (count > products.length) {//
//容器已满,需要等待消费
//通知消费者消费,生产者等待
try {
productor.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//容器不满,可以放产品
products[count] = product;
count++;
//通知消费者消费,这里与sychonized的唤醒时用的notifyAll和notify不同,用signal
consumer.signal();
} finally {
lock.unlock();//解锁
}
}
//消费者消费产品
public Product2 pop() {
lock.lock();//加锁
Product2 product;
try {
//lock.tryLock();//尝试获取锁,这个就是和synchronized区别,不会傻傻的一直等待
//判断容器有无产品,无则阻塞
if (count == 0) {
//等待生产者生产,消费者等待
try {
consumer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//可以消费,这里主要:count--必须在前,因为生产者生产之后count++会+1
count--;
product = products[count];
productor.signal();
} finally {
lock.unlock();
}
return product;
}
}