JUC高级多线程_02:线程间的通信
Posted ABin-阿斌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC高级多线程_02:线程间的通信相关的知识,希望对你有一定的参考价值。
我是 ABin-阿斌:写一生代码,创一世佳话,筑一揽芳华。 如果小伙伴们觉得我的文章有点 feel ,那就点个赞再走哦。
文章目录
声明:本文章为转载文章
- 原文请看: https://blog.csdn.net/weixin_44449838/article/details/108003617
1 . 前言-题目
- 题目: 两个线程,可以操作初始值为零的一个变量,实现一个线程对该变量 +1 ,一个线程对该变量 -1 ,实现交替,进行 10 轮,变量初始值为0
2 . 线程通信
- 生产者 + 消费者
- 消费者来消费,需要通知生产者先生产
- 生产者生产完毕,需要通知消费者消费
- 通知等待唤醒机制
3 . 多线程模板(2)
1. 根据生产者 + 消费者的工作顺序得出
- 判断: 判断有无产品
- 干活: 如果没有产品,则先生成 ; 如果已有产品,则先消费
- 通知: 通知对方我已经干完了,该你了
2. 口诀(2):判断 - 干活 - 通知
3. 模板
- 以前言的题目为例
public class JUC02_communication {
public static void main(String[] args) {
NUM num = new NUM();
new Thread(()->{
for (int i = 0; i < 10; i++){
try {
// 加法
num.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Thread_A").start();
new Thread(()->{
for (int i = 0; i < 10; i++){
try {
// 减法
num.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Thread_B").start();
}
}
class NUM{
private int number = 0;
public synchronized void increment() throws InterruptedException {
//2.1 判断
if (number != 0){
this.wait();
}
//2.2 干活
number++;
System.out.println(Thread.currentThread().getName() + "\\t"+number);
//2.3 通知(唤醒)
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//2.1 判断
if (number == 0){
this.wait();
}
//2.2 干活
number--;
System.out.println(Thread.currentThread().getName() + "\\t"+number);
//2.3 通知(唤醒)
this.notifyAll();
}
}
3 . 多线程模板(3)
1. 上述代码的问题
- 如果题目修改一下—— 两个线程执行加操作,两个线程执行减操作
- 换成4个线程会导致错误,虚假唤醒
2. 原因
- 在 java 多线程判断时,不能用if,程序出事出在了判断上面
- 突然有一添加的线程进到 if 了,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次
3. 解决办法
-
解决虚假唤醒: 我们可以根据查看 JDK1.8文档,java.lang.Object 可以看到如下说明
-
注意: 中断和虚假唤醒是可能产生的,所以要用 loop 循环,if 只判断一次。while 是只要唤醒就要拉回来再判断一次,if 判断换成 while。
4. 口诀(3):多线程交换中,必须防止多线程的虚假唤醒,也即线程中判断只能用 while
5. 修改后的代码
class NUM{
private int number = 0;
public synchronized void increment() throws InterruptedException {
//2.1 判断
while (number != 0){
this.wait();
}
//2.2 干活
number++;
System.out.println(Thread.currentThread().getName() + "\\t"+number);
//2.3 通知(唤醒)
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//2.1 判断
while (number == 0){
this.wait();
}
//2.2 干活
number--;
System.out.println(Thread.currentThread().getName() + "\\t"+number);
//2.3 通知(唤醒)
this.notifyAll();
}
}
4 . 线程通信中 synchronized 与 lock
1. synchronized
- synchronized 实现线程通信时,是使用 wait - notify / notifyAll
- 示意图
2. lock
-
lock 使用的是 Condition 对象
-
示意图
3. 模板(4)- lock
class NUM{
private int number = 0;
private Lock lock = new ReentrantLock();
//使用 lock 对应的 condition 对象
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
// lock 模块
// 上锁
lock.lock();
try {
//2.1 判断
while (number != 0){
condition.await();
}
//2.2 干活
number++;
System.out.println(Thread.currentThread().getName() + "\\t"+number);
//2.3 通知(唤醒)
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
// 开锁
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
//2.1 判断
while (number == 0){
condition.await();
}
//2.2 干活
number--;
System.out.println(Thread.currentThread().getName() + "\\t"+number);
//2.3 通知(唤醒)
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
// 开锁
lock.unlock();
}
}
}
5 . 线程间定制化调用通信(精准唤醒)
1. 题目
- 多线程之间按顺序调用
- 也就是 A 打印 1 次, B 打印 2 次, C 打印 3 次,一共进行 10 轮,且严格按照 A -> B -> C的顺序
2. 口诀(4):注意标志位的修改和定位
3. 代码演示:
public class JUC03_CustomizedCommunication {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(()->{shareResource.printA();},"Thread_A").start();
new Thread(()->{shareResource.printB();},"Thread_BB").start();
new Thread(()->{shareResource.printC();},"Thread_CCC").start();
}
}
class ShareResource{
//标志位 :A-0 ; B-1 ; C-2
private int state = 0;
private Lock lock = new ReentrantLock();
// 因为有三个线程所以需要三个 condition 对象
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
public void printA() {
print(0, conditionA, conditionB);
}
public void printB() {
print(1, conditionB, conditionC);
}
public void printC() {
print(2, conditionC, conditionA);
}
/**
* @param currentState :该线程对应的 标志位
* @param currentCondition : 当前线程 对应的 condition 对象
* @param nextCondition : 当前线程的下一个线程 对应的 condition 对象
*/
private void print(int currentState, Condition currentCondition, Condition nextCondition) {
// 循环 10 轮
for (int i = 0; i < 10; ) {
//上锁
lock.lock();
try {
while (state % 3 != currentState) {
currentCondition.await();
}
for (int j = 0; j < (state % 3 + 1); j++){
System.out.println(Thread.currentThread().getName() + " print " + (j+1));
}
state++;
i++;
nextCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
以上是关于JUC高级多线程_02:线程间的通信的主要内容,如果未能解决你的问题,请参考以下文章