面试官:请问join方法释放锁吗?
求职者:......
这个问题在面试中算是个中级问题,如果你没有深入了解join阻塞的机制,那么你很难完美地回答这个问题,OK,进入正题!
首先,要搞清楚,面试官所说的释放锁是什么场景,看个示例:
synchronized(obj) {
subThread.join()
}
对,就是这样,面试官的意思是在一段synchronized代码段内,调用join,是否会释放锁?
解析
之所以说这个问题不太好回答,是因为这种问题具备逐层深挖的特点,一般面试官的套路总是由浅入深,逐级让面试者说出问题的表象以及背后的原理。因此,面对这类问题,先给出一个不完整的回答,未尝不是一个好的选择,这样的话,可以让面试者接着问更深一层的问题,说白了,就是配合面试官完成他的套路。
join方法一般是用在这样一个场景:父线程等待子线程完成后再继续执行;即:在哪个线程的逻辑中执行join,哪个线程就等待,等待那个调用了join的线程完成。
比如:在A线程的代码中,执行了B线程的join方法,A线程就要等待B线程完成。如此说来,join是不释放锁的!——当然,这么回答也OK,但面试官马上会问:
面试官:既然不释放锁,那就是阻塞的喽,join如何实现线程阻塞的呢?
求职者:......
join阻塞原理
join实现线程阻塞的原理很简单,就是死循环!
普通的父子线程调用流程
父线程main调用子线程sub,然后两个线程同时执行各自的代码逻辑
jion调用流程
如图,父子线程仍然是各跑各的逻辑,只不过因为是在main线程中调用的join,因此main线程就被塞进了一句join的同步代码逻辑,阻塞的原理就在这个join方法中;
如图所示,join的逻辑就是一个死循环,循环判断sub线程的状态,如果sub线程状态未结束,则调用wait(0)方法,然后继续循环判断。
OK,到此又引出第三个问题:
面试官:既然最终调用的是wait方法,那为什么说join不释放锁呢?
求职者:......
源码
进入源码环节,首先要明确一点:wait一定会释放锁!这个毋庸置疑,那么之所以说join不释放锁的原因是因为wait释放的锁压根就不是外层的那个锁(对象)!
看源码,可以看到join方法被synchronized修饰,这就说明,join方法中wait释放的锁是当前线程对象,而不是join以外的锁对象。
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}