等待线程由于唤醒线程所需要的条件 永远无法成立导致线程一直无法处于RUNNABLE状态,我们就称这个线程被锁死。
虽然死锁与锁死表现出来都是线程等待,无法继续完成任务,但是产生的条件是不同的,即使在不可能产生死锁的情况下也可能出现锁死,所以不能使用对付死锁的办法来解决锁死问题。
锁死包括信号丢失锁死和嵌套监视器锁死。
信号丢失锁死的典型案例是等待线程执行Object.wait()/Condition.await()前没有对保护条件进行判断,可能没有其他的线程更新相应的保护条件涉及的变量使其成立并通知线程,使得线程一直处于等待状态。
嵌套监视锁死是嵌套锁导致线程永远无法被唤醒,如有等待方法:
public void waitMethod(){
synchronized( lock1) {
synchronized( lock2){
while( 条件 ){
lock2.wait();
}
}
//....
}
}
还有唤醒 方法如下:
public void notifyMethod(){
synchronized( lock1) {
synchronized( lock2){
lock2.notifyAll();
}
}
}
package com.wkcto.threadactivity.lockdead;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 演示嵌套监视锁死
*/
public class Test {
public static void main(String[] args) {
//创建线程,调用 putData方法向队列中存储数据
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while ( i-- < 1000){
putData("data--" + i);
try {
Thread.sleep(new Random().nextInt(50));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//创建线程调用getData方法从队列中取数据
new Thread(new Runnable() {
@Override
public void run() {
while (true){
getData();
}
}
}).start();
/*
程序运行后,不管是putData()方法还是getData()方法获得锁的顺序是相同,不可能产生死锁
运行后出现等待的情况,是因为嵌套监视锁死导致的等待
*/
}
//定义阻塞队列
private static final BlockingQueue QUEUE = new ArrayBlockingQueue<>(10);
//定义方法向队列中添加数据
public static synchronized void putData(String data){
//在同步代码块(临界区)中,调用阻塞队列QUEUE的put()/take()这些阻塞方法,可能会导致嵌套监视锁死
try {
QUEUE.put(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//定义方法从队列中取数据
public static synchronized void getData(){
try {
String data = QUEUE.take();
System.out.println("取出数据: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}