咳咳,肥壕最近...
碰上阿里的一道笔试题目,虽说题目不是非常难,但对于平时疏于练习的肥壕来说,还是花费了些许时间。
今天就特意花点时间整理一下,方便以后大家碰到这类问题能得心应手,手撕面试官。
写一个多线程,3 个线程,第一个线程打印 One,第二个打印 Two,第三个打印 Three,同时启动三个线程,要求按照 one two three 顺序打印出来
题目分析
题目本意比较简单,主要考察多线程之间的通信。3 个线程的启动是随机的,那如何让这 3 个线程按顺序打印呢。
我们知道,线程之间无非就两种关系:竞争、协同
- 竞争:每个线程去竞争打印,判断当前状态是否轮到自己打印。
- 协同:当前线程打印后,通知下一个等待的线程。
代码实现
先讲一下竞争型的实现方案,3 个线程启动后会去抢占锁,获取锁的线程会判断当前是否满足打印的条件,如果不满足则释放锁, 3 个线程再去抢占锁,如此重复。
判断条件的实现是:维护一个 state
,初始值为 0,三个线程绑定对应的值 num
分别是: 0,1,2,当 state % 3 == num
才能满足打印的条件,并且 state自增加 1 。
很显然,多线程竞争型的缺点是,线程抢占锁完全是由 CPU 调度,可能会出现不该打印的线程先被调度一直抢占锁,而该打印的线程一直未被调度,造成 CPU 性能浪费。
示例代码:
public class Test {
public static final int times = 1;
public static final int mod = 3;
public static int state = 0;
public static final Lock lock = new ReentrantLock();
static class Task implements Runnable {
private final int num;
private final String context;
public Task(int num, String context) {
this.num = num;
this.context = context;
}
@Override
public void run() {
for (int i = 0; i < times;) {
lock.lock();
if (state % mod == num) {
System.out.println(context);
state++;
i++;
}
lock.unlock();
}
}
}
public static void main(String[] args) {
Task one = new Task( 0, "one");
Task two = new Task(1, "two");
Task three = new Task(2, "three");
new Thread(one).start();
new Thread(two).start();
new Thread(three).start();
while (true) {
}
}
}
协同型的实现方式,当前线程打印完后通知下一个该打印的线程,使用通知机制理论上会有更高的执行效率。
对于多线程之间的通信,我们可以用 wait / notify
,或者 Condition 的 await / signal
方法。
示例代码:
public class Test {
public static final Object object = new Object();
static class Task implements Runnable {
private boolean flag;
private final Task preTask;
private final String context;
public Task(Task preTask, String context) {
this.preTask = preTask;
this.context = context;
}
@Override
public void run() {
synchronized (object) {
if (preTask != null && !preTask.flag) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(context);
flag = true;
object.notifyAll();
}
}
}
public static void main(String[] args) {
Task one = new Task( null, "one");
Task two = new Task(one, "two");
Task three = new Task(two, "three");
new Thread(one).start();
new Thread(two).start();
new Thread(three).start();
while (true) {
}
}
}
普通的改变,将改变普通
我是宅小年,一个在互联网低调前行的小青年
关注公众号「宅小年」,个人博客 📖 edisonz.cn,阅读更多分享文章