一、线程间的通信
(一)多线程交互的虚假唤醒
(1)虚假唤醒重现
//资源类
class AirConditioner{
private int number = 0;
public synchronized void increment() throws InterruptedException {
//1判断
if(number != 0) {
this.wait();
}
//2干活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//3通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//1判断
if(number == 0) {
this.wait();
}
//2干活
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//3通知
this.notifyAll();
}
}
// 线程操作资源类
public class ThreadWaitNotifyDemo {
public static void main(String[] args) {
AirConditioner airConditioner = new AirConditioner();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
airConditioner.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
airConditioner.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
airConditioner.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
airConditioner.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
当有两个生产者,两个消费者操作资源类,就会发生问题
原因
- A线程(生产者)获取资源类,发现number = 0,执行number++,并唤醒所有线程
- C线程(生产者)获取资源类,发现number =1,执行等待
- A继续获取操作权,发现number = 1 ,等待
- B线程获取资源类,number–,唤醒其他等待线程
- 此时A线程被唤醒,从刚才阻塞的位置继续进行,不必再if()判断了,直接number++,并唤醒其他等待线程
- C线程拿到操作权,if()中的wait()执行完毕,继续执行number++,此时出现number = 3;
(2)解决
将资源类的if()
判断更换成while()循环
判断即可,执行完wait()
继续判断是否满足条件,只有满足条件才继续执行
(二)线程通信的两种写法
synchronized()
中使用wait()
和notifyAll()
lock()
中使用Condition()
的await()
和signalAll()
两种方法的区别(为什么要用lock()替换synchronized()):
lock()
的Condition()
能够精准通知,精准唤醒
案例
多线程之间按顺序调用,实现A->B->C
三个线程启动,要求如下:
AA打印5次,BB打印10次,CC打印15次
接着
AA打印5次,BB打印10次,CC打印15次
…
// 资源类
class ShareResource{
private int number = 1; // 1:A 2:B 3:C
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
/**
* AA线程打印5次
*/
public void print5(){
lock.lock();
try{
// 1.判断
while (number !=1 ){
condition1.await();
}
// 2.干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
// 3.通知
number = 2;
condition2.signal(); //此处使print10()方法中的condition2.await()的线程被唤醒
}catch(Exception e){
}finally{
lock.unlock();
}
}
/**
* BB线程打印10次
*/
public void print10(){
lock.lock();
try{
// 1.判断
while (number!=2){
condition2.await();
}
// 2.干活
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
// 3.通知
number = 3;
condition3.signal();
}catch(Exception e){
}finally{
lock.unlock();
}
}
/**
* CC线程打印15次
*/
public void print15() {
lock.lock();
try {
// 1.判断
while (number != 3) {
condition3.await();
}
// 2.干活
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
// 3.唤醒
number = 1;
condition1.signal();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
}
public class ThreadOrderAccess {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print5();
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print10();
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print15();
}
}, "C").start();
}
}