经典面试题,使用线程实现交替打印,这里是使用的 ReentrantLock 可重入锁配合 Condition 实现交替打印。
package com.xpj.kotlingrowth.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* author : xpj
* date : 8/28/21 6:20 PM
* description :
*/
public class ABCTestNum extends Thread {
/**
* 多个线程共享这一个sequence数据
*/
private static volatile int num = 0;
// 使用 volatile 保证多线程的可见性
private static final int SEQUENCE_END = 15;
private final Integer id;
private ReentrantLock lock;
private Condition[] conditions;
ABCTestNum(Integer id, ReentrantLock lock, Condition[] conditions) {
this.id = id;
this.setName("我是Thread " + id + " 号");
this.lock = lock;
this.conditions = conditions;
}
@Override
public void run() {
// num 这里使用 volatile 可以直接不进入 while 循环
while (num >= 0 && num < SEQUENCE_END) {
// 加锁
lock.lock();
try {
// todo 加上这句才能生效,需要在 num 上加上 volatile 关键字在几个线程之间透明共享
if (num >= SEQUENCE_END) {
System.out.println("哈哈哈哈哈,啊啊啊,我到了要结束的时候了 " + id + " num : " + num);
break;
}
Thread.sleep(200);
//对序号取模,如果不等于当前线程的id,则先唤醒其他线程,然后当前线程进入等待状态
// 这里是核心,公有的数对数量取模,如果等于自己的 id 那么就不进入这个循环,否则就唤醒 +1 的那个 condition,这里不使用 while 使用 if 也可以,自己 await 就不会打印下面的了
while (num % conditions.length != id) {
System.out.println("啊啊啊,我这里的 " +id + " 不对呀,唤醒别人吧。整除唤醒啊。");
// 召唤 id + 1 的 condition ,给他发信号
conditions[(id + 1) % conditions.length].signal();
// 自己进入 await 状态
conditions[id].await();
}
System.out.println(Thread.currentThread().getName() + " 打印 ==》 " + num);
//序号加1
num = num + 1;
//唤醒当前线程的下一个线程
conditions[(id + 1) % conditions.length].signal();
//当前线程进入等待状态
conditions[id].await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//将释放锁的操作放到finally代码块中,保证锁一定会释放
lock.unlock();
}
}
System.out.println("呜啦啦啦啦,磨磨唧唧,我爱你,啊啊啊,我到了要结束的时候了 " + id);
//数字打印完毕,线程结束前唤醒其余的线程,让其他线程也可以结束
end();
}
// 第一个走到这里的线程已经 died 了, 因此别的再去发送 signal 也木有作用了,因为承载的线程已经 died 了呀。
private void end() {
System.out.println("收尾,但是也是这里导致的多打数字吗 " + id);
lock.lock();
int endSize = conditions.length - 1;
for (int i = 1; i<= endSize; i++) {
conditions[(id + i) % conditions.length].signal();
}
lock.unlock();
System.out.println("这里是收尾,after unlock " + id);
}
}
package com.xpj.kotlingrowth.thread
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.ReentrantLock
/**
* author : xpj
* date : 8/28/21 6:21 PM
* description :
*/
fun main() {
val threadCount = 4
// 使用共同的锁 ReentrantLock 可重入锁
val lock = ReentrantLock()
// 这里根据有几个线程创建几个 condition
val conditions = arrayOfNulls<Condition>(threadCount)
for (i in 0 until threadCount) {
conditions[i] = lock.newCondition()
}
// 创建对应数量的线程类
val printNumbers = arrayOfNulls<ABCTestNum>(threadCount)
for (i in 0 until threadCount) {
printNumbers[i] = ABCTestNum(i, lock, conditions)
}
// 启动线程
for (printNumber in printNumbers) {
printNumber!!.start()
}
}