1. 概述
多个线程交替打印是面试中最长考察JAVA并发相关的题目了,其目的在于考察对Java的J.U.C是否能熟练的使用。
例如:
1、3个线程交替打印A、B、C;
2、3个线程轮翻打印自然数;
2. 3个线程交替打印A、B、C
2.1 代码
package cn.pku.edu.algorithm.concurrent;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author allen
* @date 2022/9/13
*/
public class PrintCharAlternately {
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
Thread threadA = new Thread(new SyncPrinter(lock, conditionA, conditionB, 'A'), "threadA");
Thread threadB = new Thread(new SyncPrinter(lock, conditionB, conditionC, 'B'), "threadB");
Thread threadC = new Thread(new SyncPrinter(lock, conditionC, conditionA, 'C'), "threadC");
threadA.start();
Thread.sleep(1000);
threadB.start();
Thread.sleep(1000);
threadC.start();
Thread.sleep(1000);
}
}
class SyncPrinter implements Runnable {
// 打印次数
private static final int PRINT_COUNT = 10;
private final ReentrantLock reentrantLock;
private final Condition thisCondition;
private final Condition nextCondition;
private final char printChar;
public SyncPrinter(ReentrantLock reentrantLock, Condition thisCondition, Condition nextCondition, char printChar) {
this.reentrantLock = reentrantLock;
this.thisCondition = thisCondition;
this.nextCondition = nextCondition;
this.printChar = printChar;
}
@Override
public void run() {
// 获取打印锁,进入临界区
reentrantLock.lock();
try {
// 连续打印 PRINT_COUNT 次
for (int i = 0; i < PRINT_COUNT; i++) {
// 打印字符
System.out.println(Thread.currentThread().getName() + " print: " + printChar + " " + (i + 1) + " times");
// 使用nextCondition唤醒下一个线程
// 因为只有一个线程在等待,所以signal和signalAll都可以
nextCondition.signalAll();
// 不是最后一次则通过thisCondition等待被唤醒
// 必须要加判断,不然虽然能够打印10次,但是10次后就会被直接锁死
if (i < PRINT_COUNT - 1) {
try {
Thread.sleep(1000);
thisCondition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
} finally {
reentrantLock.unlock();
}
}
}
2.2 测试
3. 3个线程轮翻打印自然数
3.1 代码
package cn.pku.edu.algorithm.concurrent;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author allen
* @date 2022/9/13
*/
public class PrintNumberAlternately {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
Thread thread1 = new Thread(new SyncNumberPrinter(lock, conditionA, conditionB), "thread1");
Thread thread2 = new Thread(new SyncNumberPrinter(lock, conditionB, conditionC), "thread2");
Thread thread3 = new Thread(new SyncNumberPrinter(lock, conditionC, conditionA), "thread3");
thread1.start();
thread2.start();
thread3.start();
}
}
class SyncNumberPrinter implements Runnable {
// 所有线程公用的数字
private static volatile int num = 0;
// 每个线程打印的次数
private final int PRINT_TIMES = 1000;
private final ReentrantLock reentrantLock;
private final Condition currentCondition;
private final Condition nextCondition;
public SyncNumberPrinter(ReentrantLock reentrantLock, Condition currentCondition, Condition nextCondition) {
this.reentrantLock = reentrantLock;
this.currentCondition = currentCondition;
this.nextCondition = nextCondition;
}
@Override
public void run() {
try {
// 获取打印锁
reentrantLock.lock();
for (int i = 0; i < PRINT_TIMES; i++) {
System.out.println(Thread.currentThread().getName() + " print: " + num);
num++;
Thread.sleep(1000);
// 打印完一次后,唤醒在下一个条件下等待的线程
nextCondition.signalAll();
if (i < PRINT_TIMES - 1) {
// 打印完,当前线程释放锁,并等待在当前条件下,等待被别的线程唤醒
currentCondition.await();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放打印锁
reentrantLock.unlock();
}
}
}
3.2 测试