看到一道多线程面试题,看起来很简单但是仔细想想有些细节很容易踩坑,因此记录一下。
题目来源:https://juejin.im/post/5c89b9515188257e5b2befdd
题目:
评论:
这题用信号量数组、用Monitor(一个lock和condition数组)都能做。用信号量的解法上述链接里有,这里说下用Monitor解题存在的坑:
1. 如果代码写成了主线程初始化并start了所有线程后,直接condition.signal通知第一个线程,可能会存在问题:假如第一个线程还没有被调度、还没有执行condition.await,此时condition.signal会在condition.await之前执行,这次通知就丢掉了(但是信号量就可以先release再aquire,先通知再等待)所以为避免这种情况,还得加个countDownLatch保证所有线程的任务都执行过之后,主线程再获取锁、condition.signal通知第一个线程起跑;
2. 用condition.signal或await的前提是当前线程持有该锁(信号量就没有这种限制,可以看链接里给的信号量实现方案)
用Monitor解题的代码:
public class LoopPrinter {
private final PrintTask[]tasks;
private final int number;
private final Thread[]threads;
private final ReentrantLocklock =new ReentrantLock();
private final Condition[]conditions;
private Integercounter = -1;
private final CountDownLatchinitedLatch;
public CountDownLatch getInitedLatch() {
return initedLatch;
}
public ReentrantLock getLock() {
return lock;
}
public Condition[] getConditions() {
return conditions;
}
public LoopPrinter(int number) {
if (number <=0) {
throw new IllegalArgumentException("The number must be positive.");
}
this.number = number;
threads =new Thread[number];
tasks =new PrintTask[number];
conditions =new Condition[number];
initedLatch =new CountDownLatch(number);
for (int i =0; i < number; i++) {
PrintTask printTask =new PrintTask(this, i);
tasks[i] = printTask;
threads[i] =new Thread(printTask);
conditions[i] =lock.newCondition();
}
}
public void start() {
for (int i =0; i
threads[i].start();
}
try {
initedLatch.await();
tasks[0].go();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args)throws InterruptedException {
LoopPrinter printer =new LoopPrinter(3);
printer.start();
Thread.sleep(3000);
}
private static class PrintTaskimplements Runnable {
private LoopPrinterprinter;
private int index;
public PrintTask(LoopPrinter loopPrinter,int i) {
this.printer = loopPrinter;
index = i;
}
@Override
public void run() {
//取锁
printer.getLock().lock();
try {
while (true) {
Condition[] conditions =printer.getConditions();
printer.getInitedLatch().countDown();
Condition condition = conditions[index];
condition.await();
int counter =printer.increCounter();
if (counter >100) {
System.exit(0);
}
System.out.println("Thread " +index +":" + counter);
int nextIdx = (index +1) % conditions.length;
conditions[nextIdx].signal();
}
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
printer.getLock().unlock();
}
}
public void go() {
printer.getLock().lock();
try {
Condition[] conditions =printer.getConditions();
Condition condition = conditions[index];
condition.signal();
}finally {
printer.getLock().unlock();
}
}
}
private int increCounter() {
counter +=1;
return counter;
}
}
网友评论