顺序打印数字的进一步理解
之前博客写了篇博文,面试时要求使用多线程顺序打印ABC循环20次,这是我当时使用join函数实现代码:
public class TestABCJoin {
public static void main(String[] args) {
// 创建任务
Runnable taskA = () -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
};
Runnable taskB = () -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
};
Runnable taskC = () -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
};
// 循环 20 次
for (int i = 0; i < 20; i++) {
Thread threadA = new Thread(taskA, "A");
Thread threadB = new Thread(taskB, "B");
Thread threadC = new Thread(taskC, "C");
try {
threadA.start(); // 启动 A
threadA.join(); // 等待 A 执行完毕
threadB.start(); // 启动 B
threadB.join(); // 等待 B 执行完毕
threadC.start(); // 启动 C
threadC.join(); // 等待 C 执行完毕
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
这两天复习多线程,想到condition也可以实现这个需求,先贴上代码:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestABCLockCondition {
public static void main(String[] args) {
Alternate alternate = new Alternate();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
alternate.loopA(i);
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
alternate.loopB(i);
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
alternate.loopC(i);
}
}, "C").start();
}
}
class Alternate {
private int number = 1; // 当前应执行的线程标记:1-A, 2-B, 3-C
private final Lock lock = new ReentrantLock();
private final Condition conditionA = lock.newCondition();
private final Condition conditionB = lock.newCondition();
private final Condition conditionC = lock.newCondition();
public void loopA(int totalLoop) {
lock.lock();
try {
while (number != 1) {
conditionA.await();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
number = 2;
conditionB.signal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public void loopB(int totalLoop) {
lock.lock();
try {
while (number != 2) {
conditionB.await();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
number = 3;
conditionC.signal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public void loopC(int totalLoop) {
lock.lock();
try {
while (number != 3) {
conditionC.await();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
number = 1;
conditionA.signal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
思考了下这两种方案的优缺点(deepseek总结):
使用 join() 实现顺序打印是一种简单直观的方式,适合小规模任务。但对于高并发场景,建议使用更高效的同步机制(如 ReentrantLock 和 Condition)。
好奇具体是为什么:
可以发现,第一种方法每次循环都需要创建新的线程(new Thread()),并在任务完成后销毁线程。我们知道线程的创建和销毁是非常昂贵的操作,尤其是在高并发场景下,频繁创建线程会导致性能急剧下降。
同时join() 会阻塞当前线程(通常是主线程),直到目标线程执行完毕。
使用信号量在高并发场景下的优势:
Condition 提供了 await() 和 signal() 方法,可以精确控制线程的等待和唤醒。
ReentrantLock 和 Condition 可以实现非阻塞的线程协作,避免主线程被阻塞。