生产者消费者模式是并发、多线程编程中经典的设计模式。
简单来看,就是一个类负责生产,一个类负责消费。举例来说,一个变量,生产者不断增加这个变量,消费者不断减少这个变量。在互联网应用中,抢票机制就是应用了该模式,比如大麦网演唱会门票抢票,12306火车票抢票等。
1、生产者与消费者问题 synchronized 版本
假设有两个线程A和B,操作同一个变量,A线程+1,B线程-1, 交替循环进行
package cn.dczh.juc;
/**
* @ClassName: A
* @description: A线程 B线程 操作同一个变量 num= 0 A:+1 B:-1
* @author:
* @date: 2022/3/16 21:34
* @version: 1.0
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
}
class Data {
private int num = 0;
public synchronized void incr() throws InterruptedException {
if (num != 0) {
//等待
this.wait();
}
//加一
num++;
System.out.println(Thread.currentThread().getName() + "线程加一:" + num);
//通知其他线程 我加一完毕了
this.notifyAll();
}
public synchronized void decr() throws InterruptedException {
if (num == 0) {
//等待
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "线程减一:" + num);
//通知其他线程 我减一完毕了
this.notifyAll();
}
}
2、如果有四个线程A、B、C、D同时执行呢?(虚假唤醒问题?)
package cn.dczh.juc;
/**
* @ClassName: A
* @description: A线程 B线程 操作同一个变量 num= 0 A:+1 B:-1
* @author:
* @date: 2022/3/16 21:34
* @version: 1.0
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
class Data {
private int num = 0;
public synchronized void incr() throws InterruptedException {
if (num != 0) {
//等待
this.wait();
}
//加一
num++;
System.out.println(Thread.currentThread().getName() + "线程加一:" + num);
//通知其他线程 我加一完毕了
this.notifyAll();
}
public synchronized void decr() throws InterruptedException {
if (num == 0) {
//等待
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "线程减一:" + num);
//通知其他线程 我减一完毕了
this.notifyAll();
}
}
该代码执行结果为:
这种现象称为虚假唤醒 ! 如何解决这种问题呢?
将代码中的if判断改文文档中的while判断即可
class Data {
private int num = 0;
public synchronized void incr() throws InterruptedException {
while (num != 0) {
//等待
this.wait();
}
//加一
num++;
System.out.println(Thread.currentThread().getName() + "线程加一:" + num);
//通知其他线程 我加一完毕了
this.notifyAll();
}
public synchronized void decr() throws InterruptedException {
while (num == 0) {
//等待
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "线程减一:" + num);
//通知其他线程 我减一完毕了
this.notifyAll();
}
}
执行结果为:
3、生产者与消费者问题 JUC版本
package cn.dczh.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName: B
* @description:
* @author:
* @date: 2022/3/16 22:08
* @version: 1.0
*/
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> {
for (int i = 0; i < 10; i++) data.incr();
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) data.decr();
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) data.incr();
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) data.decr();
}, "D").start();
}
}
class Data2 {
private int num = 0;
//获取lock锁
final Lock lock = new ReentrantLock();
//通过lock锁获取对象监视器
final Condition condition = lock.newCondition();
public void incr() {
lock.lock();
try {
while (num != 0) {
//等待
condition.await();
}
//加一
num++;
System.out.println(Thread.currentThread().getName() + "线程加一:" + num);
//通知其他线程 我加一完毕了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decr() {
lock.lock();
try {
while (num == 0) {
//等待
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName() + "线程减一:" + num);
//通知其他线程 我减一完毕了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
4、Condition实现精种通知、唤醒线程
现有四个方法printA、printB、printC、printD,由四个线程A、B、C、D执行,A线程执行printA方法,执行完毕后唤醒B线程,执行printB方法,执行完毕后唤醒C线程执行printC方法,执行完毕后唤醒D线程,执行pringD方法,执行完毕后唤醒A线程 依此循环执行;
package cn.dczh.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName: C
* @description:
* @author:
* @date: 2022/3/16 22:22
* @version: 1.0
*/
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(() -> { for (int i = 0; i < 10; i++) data.printA(); }, "A").start();
new Thread(() -> { for (int i = 0; i < 10; i++) data.printB(); }, "B").start();
new Thread(() -> { for (int i = 0; i < 10; i++) data.printC(); }, "C").start();
new Thread(() -> { for (int i = 0; i < 10; i++) data.printD(); }, "D").start();
}
}
class Data3 {
//获取lock锁
final Lock lock = new ReentrantLock();
final Condition conditionA = lock.newCondition();
final Condition conditionB = lock.newCondition();
final Condition conditionC = lock.newCondition();
final Condition conditionD = lock.newCondition();
//1:A线程 2:B线程 3:C线程 4:D线程
private int flag = 1;
public void printA(){
try {
lock.lock();
while (flag != 1){
//等待
conditionA.await();
}
System.out.println("线程"+Thread.currentThread().getName()+"执行完毕!");
//修改标示为2
flag = 2;
//唤醒指定的线程 B
conditionB.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
try {
lock.lock();
while (flag != 2){
//等待
conditionB.await();
}
System.out.println("线程"+Thread.currentThread().getName()+"执行完毕!");
//修改标示为2
flag = 3;
//唤醒指定的线程 C
conditionC.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
try {
lock.lock();
while (flag != 3){
//等待
conditionC.await();
}
System.out.println("线程"+Thread.currentThread().getName()+"执行完毕!");
//修改标示为2
flag = 4;
//唤醒指定的线程 D
conditionD.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printD(){
try {
lock.lock();
while (flag != 4){
//等待
conditionD.await();
}
System.out.println("线程"+Thread.currentThread().getName()+"执行完毕!");
//修改标示为2
flag = 1;
//唤醒指定的线程 A
conditionA.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
执行结果如下:
小结
锁:锁的是new this 锁的是对象,static Clazz 锁的是唯一的类模板;