线程间通信的模型有两种:共享内存 和 消息传递,以下方式都是基本这两种模型来实现的。
我们来基本一道面试常见的题目来分析:
对一个值两个线程对其交替 +1 -1 (线程间通信)
3.1 synchronized 方案
package juc.wait3;import java.util.concurrent.Callable;/*** @Author Rock Wang* @Time 2021/11/3*/public class Main {public static void main(String[] args) {Share share = new Share();new Thread(() -> {for (int i = 0; i < 30; i++) {try {share.incr();} catch (InterruptedException e) {e.printStackTrace();}}}, "AA").start();new Thread(() -> {for (int i = 0; i < 30; i++) {try {share.decr();} catch (InterruptedException e) {e.printStackTrace();}}}, "BB").start();}}/*** Share 共享*/class Share {private int number = 0;/*** +1*/public synchronized void incr() throws InterruptedException {//判断 干活 通知//if (number != 0) {while (number != 0) {this.wait();}number++;System.out.println(Thread.currentThread().getName() + ",number :" + number);//唤醒其他所有线程this.notifyAll();}/*** -1*/public synchronized void decr() throws InterruptedException {//if (number != 1) {while (number != 1) {this.wait();}number--;System.out.println(Thread.currentThread().getName() + ",number :" + number);//唤醒其他所有线程this.notifyAll();}}
如果线程超过两个,if判断的过程会出现虚假唤醒问题,导致无法交替加减。
wait() 在哪里睡,在哪里被唤醒,导致if无效,更换while循环即可。
四个线程
AA ++
BB —
CC ++
DD —
都是调用start方法创建,但是先后顺序不一定,睡眠唤醒也不一定。
3.2 Lock 方案
Condition 状况,状态;条件,环境
package juc.lock2;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*** @Author Rock Wang* @Time 2021/11/3*/public class Await4 {public static void main(String[] args) {Share share = new Share();for (int i = 0; i < 20; i++) {new Thread(() -> {try {share.incr();} catch (InterruptedException e) {e.printStackTrace();}}, "AA").start();new Thread(() -> {try {share.decr();} catch (InterruptedException e) {e.printStackTrace();}}, "BB").start();new Thread(() -> {try {share.incr();} catch (InterruptedException e) {e.printStackTrace();}}, "CC").start();new Thread(() -> {try {share.decr();} catch (InterruptedException e) {e.printStackTrace();}}, "DD").start();}}}/*** Condition 状况,状态;条件,环境*/class Share {private int number = 0;private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();/*** ++*/public void incr() throws InterruptedException {//上锁lock.lock();try {//判断 干活 通知while (number != 0) {//等待condition.await();}number++;System.out.println(Thread.currentThread().getName() + ",number :" + number);condition.signalAll();} finally {//解锁lock.unlock();}}/*** --*/public void decr() throws InterruptedException {lock.lock();try {while (number != 1) {condition.await();}number--;System.out.println(Thread.currentThread().getName() + ",number :" + number);condition.signalAll();} finally {lock.unlock();}}}
3.3线程间定制化通信
==问题: A线程打印5次A,B线程打印10次B,C线程打印15次C,按照此顺序循环10轮==
关键点:需要让线程按照顺序执行。
1.分别加标志位 flag = 1 2 3
2.判断标志位 1,打印
3.修改标志位 2,通知BB
4.判断标志位 2,打印
5.修改标志位 3,通知CC
6.判断标志位 3,打印
7.修改标志位 1,通知AA
…循环10次
package juc.lock2;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*** @Author Rock Wang* @Time 2021/11/3*/public class ThreadDemo3 {public static void main(String[] args) {ShareResult shareResult = new ShareResult();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {shareResult.print5(i);} catch (InterruptedException e) {e.printStackTrace();}}}, "AA").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {shareResult.print10(i);} catch (InterruptedException e) {e.printStackTrace();}}}, "BB").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {shareResult.print15(i);} catch (InterruptedException e) {e.printStackTrace();}}}, "CC").start();}}/*** 创建资源类*/class ShareResult {/*** 1:AA* 2:BB* 3:CC*/private int flag = 1;/*** 创建Lock锁*/private Lock lock = new ReentrantLock();/*** 创建三个Condition*/private Condition c1 = lock.newCondition();private Condition c2 = lock.newCondition();private Condition c3 = lock.newCondition();/*** 打印五次** @param loop 第几轮*/public void print5(int loop) throws InterruptedException {//上锁lock.lock();try {//判断while (flag != 1) {c1.await();}for (int i = 0; i < 5; i++) {// 干活System.out.println(Thread.currentThread().getName() + " :: " + (i + 1) + ",轮数: " + loop);}//修改标志位flag = 2;//通知c2.signalAll();} finally {lock.unlock();}}/*** 打印10次** @param loop 第几轮*/public void print10(int loop) throws InterruptedException {//上锁lock.lock();try {//判断while (flag != 2) {c2.await();}for (int i = 0; i < 10; i++) {// 干活System.out.println(Thread.currentThread().getName() + " :: " + (i + 1) + ",轮数: " + loop);}//修改标志位flag = 3;//通知c3.signalAll();} finally {lock.unlock();}}/*** 打印15次** @param loop 第几轮*/public void print15(int loop) throws InterruptedException {//上锁lock.lock();try {//判断while (flag != 3) {c3.await();}//干活for (int i = 0; i < 15; i++) {System.out.println(Thread.currentThread().getName() + " :: " + (i + 1) + ",轮数: " + loop);}//修改标志位flag = 1;//通知c1.signalAll();} finally {lock.unlock();}}}
