生产者消费者问题
1109字约4分钟
2024-08-08
线程之间的通信问题:生产者和消费者问题
Synchronized 版
/**
* 线程之间的通信问题:生产者和消费者问题。等待唤醒,通知唤醒
* 线程 A B 交替执行,操作同一个变量 number
* A number + 1
* B number - 1
*/
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
// 生产消费者问题口诀:判断等待,业务,通知
class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
// 判断等待
if (number != 0) {
this.wait();
}
// 业务
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,业务执行完毕
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
// 判断等待
if (number == 0) {
this.wait();
}
// 业务
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,业务执行完毕
this.notifyAll();
}
}
如果再多来几个线程同时执行,存在虚假唤醒问题
public final void wait()
throws InterruptedException
导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。 换句话说,这个方法的行为就好像简单地执行呼叫wait(0) 。
当前的线程必须拥有该对象的显示器。 该线程释放此监视器的所有权,并等待另一个线程通知等待该对象监视器的线程通过调用notify方法或notifyAll方法notifyAll 。 然后线程等待,直到它可以重新获得监视器的所有权并恢复执行。
像在一个参数版本中,中断和虚假唤醒是可能的,并且该方法应该始终在循环中使用:
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
该方法只能由作为该对象的监视器的所有者的线程调用。 有关线程可以成为监视器所有者的方式的说明,请参阅notify方法。
异常
IllegalMonitorStateException - 如果当前线程不是对象监视器的所有者。
InterruptedException - 如果任何线程在当前线程等待通知之前或当前线程中断当前线程。 当抛出此异常时,当前线程的中断状态将被清除。
if
判断改为 while
循环
// 生产消费者问题口诀:判断等待,业务,通知
class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
// 判断等待
while (number != 0) {
this.wait();
}
// 业务
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,业务执行完毕
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
// 判断等待
while (number == 0) {
this.wait();
}
// 业务
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,业务执行完毕
this.notifyAll();
}
}
JUC 版本
// 生产消费者问题口诀:判断等待,业务,通知
class Data2 {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() {
lock.lock();
try {
// 判断等待
while (number != 0) {
condition.await();
}
// 业务
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,业务执行完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
// 判断等待
while (number == 0) {
condition.await();
}
// 业务
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,业务执行完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.increment();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.decrement();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.increment();
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.decrement();
}
}, "D").start();
}
A=>1
B=>0
C=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
观察结果发现没问题,但是四个线程的执行是随机的,如何控制 A
执行完 B
执行 C
执行 D
执行
Condition 精准的通知和唤醒线程
A
B
C
三个线程,A
执行完 B
执行再 C
执行
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printC();
}
}, "C").start();
}
class Data3 {
private int num = 1;
private Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
public void printA() {
lock.lock();
try {
// 条件判断
while (num != 1) {
conditionA.await();
}
// 业务
num = 2;
System.out.println(Thread.currentThread().getName() + "=> AAAAA");
// 唤醒(精准唤醒B)
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (num != 2) {
conditionB.await();
}
num = 3;
System.out.println(Thread.currentThread().getName() + "=> BBBBB");
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (num != 3) {
conditionC.await();
}
num = 1;
System.out.println(Thread.currentThread().getName() + "=> CCCCC");
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}