Java多线程-线程交替打印

今日复习来自极海Channel的一个面试

如何实现两个线程实现交替打印,线程A打印A,线程B打印B?

讲真当时看到的时候,脑子里面第一反应就是信号量和synchronized解法,那有synchronized就会有ReentrantLock,那就尝试用这三个解法回答一下吧。

代码地址:线程交替打印

1、synchronized实现

synchronized的方式实现主要是进行加锁,通过一把对象锁,在代码块内只允许一个线程执行后续操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Object lock = new Object();

Thread threadA = new Thread(new SynchronizedPrintThread(lock, "A", 0), "线程A");
Thread threadB = new Thread(new SynchronizedPrintThread(lock, "B",1), "线程B");
Thread threadC = new Thread(new SynchronizedPrintThread(lock, "C",2), "线程C");

threadA.start();
threadB.start();
threadC.start();


class SynchronizedPrintThread implements Runnable {
private final Object lock;
private final String message;
private final int order;
private static int currentOrder = 0;

public SynchronizedPrintThread(Object lock, String message, int order) {
this.lock = lock;
this.message = message;
this.order = order;
}

@Override
public void run() {
try {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
// 打印顺序就是A-->B-->C
while (currentOrder % 3 != order) {
lock.wait(); // 当前线程等待,直到轮到自己打印
}
System.out.println(Thread.currentThread().getName() + "------" + message);
currentOrder++;
lock.notifyAll(); // 唤醒其他等待的线程
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

2、ReentrantLock实现

ReentrantLock实现,同样会传入锁,但是lock和unlock是自己规定,所以当需要判断打印的时候,进行加锁操作,同时每个线程进去之后判断是否是自己需要打印,如果不是,那么就唤醒下一个线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
ReentrantLock lock = new ReentrantLock();

Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();

Thread threadA = new Thread(new ReentrantLockPrintThread(lock, conditionA, conditionB, "A", 0));
Thread threadB = new Thread(new ReentrantLockPrintThread(lock, conditionB, conditionC, "B", 1));
Thread threadC = new Thread(new ReentrantLockPrintThread(lock, conditionC, conditionA, "C", 2));

threadA.start();
threadB.start();
threadC.start();


class ReentrantLockPrintThread implements Runnable {
private final Lock lock;
private final Condition current;
private final Condition next;
private final String message;
private final int order;

// 多个线程共享,如果没有static的话,那么会造成只有第一个线程执行了,其余线程还在等待第一个线程执行。
private static int currentOrder = 0;

public ReentrantLockPrintThread(Lock lock, Condition currentCondition, Condition nextCondition, String message, int order) {
this.lock = lock;
this.current = currentCondition; //当前线程信号量
this.next = nextCondition; //下一个线程信号量
this.message = message;
this.order = order;
}

@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
// 某个线程持有锁,只有一个线程进入后续部分
lock.lock();
// 条件判断,如果不满足,当前线程等待,
while (currentOrder % 3 != order) {
current.await();
}
System.out.println(Thread.currentThread().getName() + "------" + message);
currentOrder++;
// 使用signal()唤醒下一个线程
next.signal();
// 锁释放
lock.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

注意:Condition对象是与锁(ReentrantLock)关联的条件对象,用于线程间的等待和通知机制。因此锁的类型不并不是Object

1
2
3
4
5
ReentrantLock lock = new ReentrantLock();

lock.newContional(); //ConditionalA
lock.newContional(); //ConditionalB
lock.newContional(); //ConditionalC

3、信号量实现

在使用信号量实现的时候,需要指定谁最先拥有许可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Semaphore semaphoreA = new Semaphore(1);
Semaphore semaphoreB = new Semaphore(0);
Semaphore semaphoreC = new Semaphore(0);

Thread threadA = new Thread(new SemaphorePrintThread(semaphoreA, semaphoreB, "A", 0));
Thread threadB = new Thread(new SemaphorePrintThread(semaphoreB, semaphoreC, "B", 1));
Thread threadC = new Thread(new SemaphorePrintThread(semaphoreC, semaphoreA, "C", 2));

threadA.start();
threadB.start();
threadC.start();


class SemaphorePrintThread implements Runnable {
private final Semaphore current;
private final Semaphore next;
private static int currentOrder = 0;

private final String message;
private final int order;

public SemaphorePrintThread(Semaphore current, Semaphore next, String message, int order) {
this.current = current;
this.next = next;
this.message = message;
this.order = order;
}

@Override
public void run() {
try {

for (int i = 0; i < 10; i++) {
/**
* 核心:通过获取当前线程的信号量来确定是否轮到自己执行。
* 在执行完打印操作后,释放下一个线程的信号量,从而实现线程的交叉打印。
*/
current.acquire(); // 获取当前线程的信号量
System.out.println(Thread.currentThread().getName() + "------" + message);
currentOrder++;
next.release(); //释放下一个线程的信号量
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

Java多线程-线程交替打印
https://baijianglai.cn/Java多线程-线程交替打印/4fde684e52ae/
作者
Lai Baijiang
发布于
2023年7月5日
许可协议