⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/qq_35387940/article/details/128828799 「小目标青年」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

一个老掉牙的java面试问题 , 多线程交替打印。

有打印 ABC 的, 有打印 123 的, 有打印到100的 。

其实都一样。

ps: 最近好多小伙伴问这个,这个题这么热门么?

实例实战思路

拿一个来做示例, 就交替打印ABC. (文末也说下从1到100的)

一起看看这个小题目 :

主角

三个线程 线程A 线程 B 线程 C

要做的事

交替打印 A B C

那就是 线程A 负责打印 A ,线程 B 负责打印 B ,线程C 负责打印 C 。

简单分析

A线程打印完, B线程 打印 ;

B线程打印完 ,C 线程打印。

也就是说,这3个线程 ABC ,有顺序/协作 。

那么这三个家伙 怎么知道自己要打印东西呢?

那必然是通知等待

思路

三个线程都 准备就绪, 准备大展身手。

接到携带暗号的通知(默认暗号为 A) -> 核对通知的暗号 -> 打印->然后修改暗号-> 发出携带暗号通知(让别的线程认领)

如果接到通知,发现暗号不对,怎么办呢? 继续进入等待,等待下个通知的到来。

简图

三个线程的一举一动 :

ps:

  • 如果是线程A打印完,就把通知暗号改成B,并发出通知;
  • 如果是线程B打印完,就把通知暗号改成C,并发出通知;
  • 同样,如果是线程C打印完,就把通知改回A,继续发通知。

代码

写个ReentrantLock条件控制来搞一下。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;


public class DoTest {

//控制三个线程 ABC,保证同一时刻只有一个线程工作
private static Lock lock = new ReentrantLock(true);

// Condition ,控制等待或是 通知
private static Condition conditionA = lock.newCondition();
private static Condition conditionB = lock.newCondition();
private static Condition conditionC = lock.newCondition();

//默认的通知 暗号 A
private static String CODE= "A";


public static void main(String[] args) {

Thread A = new Thread(() -> {
while (true) {
try {
printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

Thread B = new Thread(() -> {
while (true) {
try {
printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

Thread C = new Thread(() -> {
while (true) {
try {
printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

A.start();
B.start();
C.start();
}

public static void printA() throws InterruptedException {

//等待
lock.lock();
try {
//核对
while (!CODE.equals("A")) {
//暗号不对,就进入等待
conditionA.await();
}
System.out.println("A");
//改暗号,通知B
CODE = "B";
conditionB.signalAll();
} finally {
lock.unlock();
}
}

public static void printB() throws InterruptedException {
lock.lock();
try {
while (!CODE.equals("B")) {
conditionB.await();
}
System.out.println("B");
//改暗号,通知C
CODE = "C";
conditionC.signalAll();
} finally {
lock.unlock();
}

}

public static void printC() throws InterruptedException {
lock.lock();
try {
while (!CODE.equals("C")) {
conditionC.await();
}
System.out.println("C");

//改暗号,通知A
CODE = "A";
conditionA.signalAll();
} finally {
lock.unlock();
}

}


}

效果:

可以看到,三个线程 ABC 都开始 无休止的进行了 等待 -接通知 -核对- 打印-改暗号发通知 。

当然如果需要他们不这么无休止,只需要 做一个标识进行判断就好,例如 加在一起已经打印够100次了,就停止 之类的限制值。

举例, 如果交替打印,到100 就停止, 也就是 从1~100 线程A ,线程B ,线程 B 交替打印。

ok,代码稍作调整 :

加上2个值

一个是打印的数字,这个会一直 +1 输出;

一个是用于线程循环的,之前是while(true) ,这样会一直跑。

如果 终止标记还是false,就继续执行:

每个打印方法都加上判断和累计+1的代码:

看看效果:

整体代码贴一下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;


public class DoTest {

//控制三个线程 ABC,保证同一时刻只有一个线程工作
private static Lock lock = new ReentrantLock(true);

// Condition ,控制等待或是 通知
private static Condition conditionA = lock.newCondition();
private static Condition conditionB = lock.newCondition();
private static Condition conditionC = lock.newCondition();

//默认的通知 暗号 A
private static String CODE = "A";

//设置初始打印值
private static int COUNT_NUM = 1;

//线程是否需要终止 标记
private static volatile boolean IS_INTERRUPT = false;

public static void main(String[] args) {

Thread A = new Thread(() -> {
while (!IS_INTERRUPT) {
try {
printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

Thread B = new Thread(() -> {
while (!IS_INTERRUPT) {
try {
printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

Thread C = new Thread(() -> {
while (!IS_INTERRUPT) {
try {
printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

A.start();
B.start();
C.start();
}

public static void printA() throws InterruptedException {

//等待
lock.lock();
try {
if (COUNT_NUM >= 100) {
IS_INTERRUPT = true;
return;
}
//核对
while (!CODE.equals("A")) {
//暗号不对,就进入等待
conditionA.await();
}
System.out.println("A, count" + COUNT_NUM);
//改暗号,通知B
CODE = "B";
COUNT_NUM = COUNT_NUM + 1;
conditionB.signalAll();
} finally {
lock.unlock();
}
}

public static void printB() throws InterruptedException {
lock.lock();
try {

if (COUNT_NUM >= 100) {
IS_INTERRUPT = true;
return;
}

while (!CODE.equals("B")) {
conditionB.await();
}

System.out.println("B, count" + COUNT_NUM);
//改暗号,通知C
CODE = "C";
COUNT_NUM = COUNT_NUM + 1;

conditionC.signalAll();
} finally {
lock.unlock();
}

}

public static void printC() throws InterruptedException {
lock.lock();
try {

if (COUNT_NUM >= 100) {
IS_INTERRUPT = true;
return;
}

while (!CODE.equals("C")) {
conditionC.await();
}

System.out.println("C, count" + COUNT_NUM);
//改暗号,通知A
CODE = "A";
COUNT_NUM = COUNT_NUM + 1;
conditionA.signalAll();
} finally {
lock.unlock();
}

}

}

可能看到这里,有些初学者会心有疑惑,因为他没了解过 ReentrantLock、Lock 、Condition ,就觉得这个看了不踏实。

没事的,其实只是为了给大家实打实分析思路, 用synchronized 配合 wait() 和 notifyAll 也是一样的。

notifyAll 大不了就全部通知唤醒,然后自己核对暗号再进入等待。

示例,上代码:

public class DoTest1 {
private volatile int COUNT_NUM = 1;
private volatile String CODE = "A";
private static int oneTimes = 34;
private static int othersTimes = 33;

void onePrint() {
synchronized (this) {
while(!CODE.equals("A")) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": " + COUNT_NUM);
COUNT_NUM++;
CODE = "B";
notifyAll();
}
}
void twoPrint() {
synchronized (this) {
while(!CODE.equals("B")) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": " + COUNT_NUM);
COUNT_NUM++;
CODE = "C";
notifyAll();
}
}
void threePrint() {
synchronized (this) {
while(!CODE.equals("C")) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": " + COUNT_NUM);
COUNT_NUM++;
CODE = "A";
notifyAll();
}
}


public static void main(String[] args) {
DoTest1 printNumber = new DoTest1();
new Thread(() -> {
for (int i = 0; i < oneTimes; i++) {
printNumber.onePrint();
}
},"线程A").start();

new Thread(() -> {
for (int i = 0; i < othersTimes; i++) {
printNumber.twoPrint();
}
},"线程B").start();

new Thread(() -> {
for (int i = 0; i < othersTimes; i++) {
printNumber.threePrint();
}
},"线程C").start();
}
}

代码简析:

看看效果:

好了,该篇就到这吧。

文章目录
  1. 1. 实例实战思路
  2. 2. 主角
  3. 3. 要做的事
  4. 4. 简单分析
  5. 5. 思路
  6. 6. 简图
  7. 7. 代码