提到线程通信,就不得不提经典的 生产者消费者模式
1. 生产者消费者模式
实现双线程交替打印 0-1
public class ConProMode {public static void main(String[] args) {source s = new source();
new Thread(()-> { // 生产者线程
for(int i=0; i<10; i++){
try {s.increase();
} catch (InterruptedException e) {e.printStackTrace();
}}
},"生产者 A").start();
new Thread(()-> { // 消费者线程
for(int i=0; i<10; i++){
try {s.decrease();
} catch (InterruptedException e) {e.printStackTrace();
}}
},"消费者 A").start();}
}
class source{ // 资源类
private int num = 0;
public synchronized void increase() throws InterruptedException {
// 1. 判断
if(num != 0){ // 此处应该用 while
this.wait();}
// 2. 外围业务
num++;
System.out.println(Thread.currentThread().getName() + "\t" + num);
// 3. 线程通信
this.notifyAll();}
public synchronized void decrease() throws InterruptedException {if(num == 0){this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "\t" + num);
this.notifyAll();}
}
附上最近学习对我了解和编写多线程晋升最大的几句话:
- 高内聚低耦合,线程操控资源类
- 操控资源办法集成在资源类中,给线程调用
2. 线程间的虚伪唤醒景象
在 1 中的生产者消费者模式中,因为生产者和消费者都只有一个,因而不会呈现虚伪唤醒问题;而如果将 生产者和消费者都设置为为两个,即共 4 条线程操控同一份资源且彼此有交互(即通信),则将会呈现线程的虚伪唤醒景象,机制可见下图:
起因是在线程被唤醒后,如果应用 if 判断的话,因为线程 wait 前曾经通过一次 if 判断,而 if 语句是一个一次性判断,线程昏迷后不会再次执行判断,而是间接执行前面的业务语句,这样就可能导致本该同步的操作却失去了同步性能。因而,在多线程的程序中,控制线程状态的判断语句肯定要应用 while,实现反复判断,防止虚伪唤醒的问题
3. Lock 实现线程的精准通信
Syncronized 同步监视器是一种偏心的锁机制 ,即当线程开释锁后,监视器上的所有线程都有权力去抢夺这把锁并获取执行权,即线程的执行是无序的(除非有且仅有两条线程,能够交替执行),但很多理论利用场景所须要的线程之间的交互是非偏心的,而是有序的,这就须要监视器为其上的每条线程特备一个认证操作通道。艰深了解 为锁对象为每条线程都装备一把钥匙,各线程能够在本人的工作中抉择和特定的线程进行通信,以此达到让线程按指定程序执行。Lock 锁便实现了这种机制。
举例:
/**
* A - B - C 三个线程
* 实现要求:AA 间断打印 1 次 而后 BB 间断打印 2 次 而后 CC 间断打印 3 次
* 反复 5 轮
*/
public class ConProDemo03 {public static void main(String[] args) {source03 s = new source03();
new Thread(()->{ for(int i=0; i<5; i++) s.printA();},"线程 A").start();
new Thread(()->{ for(int i=0; i<5; i++) s.printB();},"线程 B").start();
new Thread(()->{ for(int i=0; i<5; i++) s.printC();},"线程 C").start();}
}
class source03{
private int flag = 1;
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition(); // 为线程 A 配一把钥匙
private Condition condition2 = lock.newCondition(); // 为线程 B 配一把钥匙
private Condition condition3 = lock.newCondition(); // 为线程 C 配一把钥匙
public void printA() {lock.lock();
try{while(flag != 1){condition1.await();
}
for(int i=0; i<1; i++){System.out.println(Thread.currentThread().getName() + "\t" + "AA");
}
flag = 2;
condition2.signal(); // 实现对线程的精准唤醒}catch (InterruptedException e){e.printStackTrace();
}finally {lock.unlock();
}
}
public void printB() {lock.lock();
try{while(flag != 2){condition2.await();
}
for(int i=0; i<2; i++){System.out.println(Thread.currentThread().getName() + "\t" + "BB");
}
flag = 3;
condition3.signal();}catch (InterruptedException e){e.printStackTrace();
}finally {lock.unlock();
}
}
public void printC() {lock.lock();
try{while(flag != 3){condition3.await();
}
for(int i=0; i<3; i++){System.out.println(Thread.currentThread().getName() + "\t" + "CC");
}
flag = 1;
condition1.signal();}catch (InterruptedException e){e.printStackTrace();
}finally {lock.unlock();
}
}
}