其实 老版的 和 新版的 解决生产者消费者的思路几乎不差
主要区别就是下列:
老版 this.wait() this.notifyAll()
JUC版 condition.await() condition.signalAll()
传统版
我们在只有两个线程(一个生产线程,一个消费线程)的类中,用if来进行判断/循环 会发现 ,唉好像没什么问题,一切都是正常进行的样子,那我们在多来一个线程呢? 你还能保证你的线程安全吗?
首先我们来看官方文档:
从文档中我们可以很清晰的看到 使用if 进行判断会造成一个 ”虚假唤醒问题“ --> 线程可以唤醒,中断或超时
解决方法 :用while 判断
if判断–> 虚假唤醒
/**
* @Author 码农天宇
* @Version 1.0
*/
public class PC {
/*
线程通信问题: 生产者和消费者! --> 等待唤醒,通知唤醒
*/
public static void main(String[] args) {
Data d = new Data();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++){
d.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c1").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++){
d.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c2").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++){
d.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c3").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++){
d.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c4").start();
}
}
// 资源类
class Data{
private int num = 0;
// 生产者生产商品
public synchronized void increment() throws InterruptedException {
if ( num != 0) {
// 等待
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "正在生产: " + num);
// 通知其他线程,我生产完毕了
this.notifyAll();
}
// 消费者消费商品
public synchronized void decrement() throws InterruptedException {
if (num==0) {
// 等待生产
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "正在消费: " + num);
// 通知其他线程,我消费完毕了
this.notifyAll();
}
}
if循环导致问题 --> 虚假唤醒
while解决唤醒
用whie 循环即可解决
/**
* @Author 码农天宇
* @Version 1.0
*/
public class PC {
/*
线程通信问题: 生产者和消费者! --> 等待唤醒,通知唤醒
*/
public static void main(String[] args) {
Data d = new Data();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
d.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c1").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
d.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c2").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
d.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c3").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
d.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c4").start();
}
}
// 资源类
class Data{
private int num = 0;
// 生产者生产商品
public synchronized void increment() throws InterruptedException {
while ( num != 0) {
// 等待
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "正在生产: " + num);
// 通知其他线程,我生产完毕了
this.notifyAll();
}
// 消费者消费商品
public synchronized void decrement() throws InterruptedException {
while (num==0) {
// 等待生产
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "正在消费: " + num);
// 通知其他线程,我消费完毕了
this.notifyAll();
}
}
这样看来,我们的问题就会得到解决
JUC版
没有顺序的/不可控制的(多线程环境下)
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author 码农天宇
* @Version 1.0
*/
public class PC {
/*
线程通信问题: 生产者和消费者! --> 等待唤醒,通知唤醒
*/
public static void main(String[] args) {
Data d = new Data();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
d.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c1").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
d.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"c2").start();
}
}
// 资源类
class Data{
private int num = 0;
// 可重入锁
Lock lock = new ReentrantLock();
// Condition 精准的唤醒和通知线程
Condition cond = lock.newCondition();
// 生产者生产商品
public void increment() throws InterruptedException {
// 加锁
lock.lock();
try {
while ( num != 0) {
// 等待
cond.await();
}
num++;
System.out.println(Thread.currentThread().getName() + "正在生产: " + num);
// 通知其他线程,我生产完毕了
cond.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
// 消费者消费商品
public void decrement() throws InterruptedException {
// 加锁
lock.lock();
try {
while (num==0) {
// 等待生产
cond.await();
}
num--;
System.out.println(Thread.currentThread().getName() + "正在消费: " + num);
// 通知其他线程,我消费完毕了
cond.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
}
我们可以看到,他是随机的,我们不可控制的
有顺序的(多线程环境下)
我们假设有3个线程 : c1,c2,c3
我们需要c1线程跑完执行c2线程,c2线程跑完执行c3线程,c3线程跑完执行c1线程
那么我们需要怎么实现?
通过Condition来实现 --> 它可以精准的 ,唤醒通知线程
Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
我们通过lock来实现3个监听器
通过flag状态来精准的选择,通知下一步执行那个线程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author 码农天宇
* @Version 1.0
*/
public class PC {
public static void main(String[] args) {
/*
A执行完调用B,B执行完调用C,C执行完调用A
*/
Data da = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
da.printA();
}
},"c1").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
da.printB();
}
},"c2").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
da.printC();
}
},"c3").start();
}
}
class Data{
Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
// 状态 flag
int flag = 1;
// 顺序执行
public void printA(){
lock.lock();
try {
while ( flag != 1) {
c1.await();
}
System.out.println(Thread.currentThread().getName() + ": AAA");
// 唤醒指定的线程 B
flag = 2;
c2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (flag != 2) {
c2.await();
}
System.out.println(Thread.currentThread().getName() + ": BBB");
flag = 3;
// 唤醒线程c3
c3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (flag != 3) {
c3.await();
}
System.out.println(Thread.currentThread().getName() + ": CCC");
flag = 1;
// 唤醒指定线程 c1
c1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}