最近做的一个项目使用Java编写,在调试中遇到两种因为调用join()引发的死锁情形,很隐蔽。记录于此。
1.线程join自身发生死锁
public class Starter {
public static void main(String[] args) {
new DeadThread().start();
}
}
class DeadThread {
public DeadThread() {
thread = new Thread(new RealThread());
}
public void start() {
thread.start();
}
private Thread thread;
private class RealThread implements Runnable{
public void run() {
System.out.println("Thread is starting...");
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread is stopping...");
}
}
}
线程RealThread调用thread.join()本是想与另一个线程同步(示例代码中没有展示),但正如示例代码表现的那样,thread实际指向了它自身,这就形成了一种诡异的死锁情形:一个正在运行中的线程调用join()来等待自己结束。
2.锁的维护不当
相比上面的情形,下面的情景则更为常见。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Starter {
public static void main(String[] args) {
DeadThread dt = new DeadThread();
dt.start();
dt.stop();
}
}
class DeadThread {
public DeadThread() {
thread = new Thread(new RealThread());
lock = new ReentrantLock();
}
public void start() {
thread.start();
}
public void stop() {
lock.lock();
try {
// do some real works...
running = false;
thread.join(); // (2)
} catch(Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private Thread thread;
private Lock lock;
private volatile boolean running = true;
private class RealThread implements Runnable{
public void run() {
while (running) { // (1)
lock.lock(); // (3)
try {
// do some real works...
System.out.println("Thread is running...");
} finally {
lock.unlock();
}
}
}
}
}
上面的代码在大部分情况下都能正常工作,但是死锁的情形依然存在。线程以如下的顺序执行时,就出现了死锁:
<1>RealThread线程执行到(1)处,即while(running)执行完时,被DeadThread线程抢占
<2>DeadThread线程调用stop()函数,而此时的thread指向<1>中的RealThread线程,当stop()函数执行到(2)处,会等待RealThread的结束,放弃CPU
<3>RealThread线程恢复执行,此时因为DeadThread占有lock锁,所以RealThread会在(3)处等待线程DeadThread释放lock
在<3>执行完后,DeadThread线程和RealThread线程互相等待,便发生死锁。