Java死锁问题学习笔记
设计一个案例:
多线程运行环境下,假设程序要访问一个共享变量,对共享变量计算的中间结果用打印机打印出来,而打印机一次只允许一个线程访问
分析:
显然,一个线程要获取共享变量的访问权(对被用来同步访问的共享变量那把锁加锁——lockA),此外还要获取对打印机的访问权(对被用来同步访问打印机的锁成功加锁——lockB)。
public class Demo1 {
static Object lockA = new Object();
static Object lockB = new Object();
public static void main(String[] args) {
ABThread abThread = new ABThread();
BAThread baThread = new BAThread();
abThread.start();
baThread.start();
}
}
// 向访问共享变量,在访问打印机,将对共享变量计算的结果发送到打印机
class ABThread extends Thread {
@Override
public void run() {
// 首先,尝试访问共享变量
synchronized (Demo1.lockA) {
try {
Thread.sleep(2000);
System.out.println("ABThread 加 lockA锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 访问共享变量做计算,得到一些结果
synchronized (Demo1.lockB) {
// 把共享变量计算的结果发送到打印机来打印
System.out.println("ABThread 加 lockB锁");
}
}
}
}
// 先获取打印机的访问权,在尝试访问共享变量,将共享变量计算结果发送到打印机打印
class BAThread extends Thread {
@Override
public void run() {
// 首先,尝试访问打印机
synchronized (Demo1.lockB) {
// 获取打印机的访问权
try {
Thread.sleep(2000);
System.out.println("BAThread 加 lockB锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Demo1.lockA) {
//访问共享变量, 计算出结果,把结果发送到打印机打印
System.out.println("BAThread 加 lockA锁");
}
}
}
}
运行结果:
可以看出程序没有运行结束(发生了死锁现象)
死锁的问题:
死锁是指两个以上的线程在执行过程中,因为争夺资源而产生的一种相互等待的现象。
死锁问题的两种常见解决方式:
1.将多线程获取锁的顺序调整相同
2.再定义一把新的锁,利用这把锁(synchronized+该对象),实现将加多把锁的操作变成一个原子操作
(原子操作即一组不可分割的操作,要么全做,要么一点也不做)
解决方案一的代码实现:
public class Demo1 {
static Object lockA = new Object();
static Object lockB = new Object();
public static void main(String[] args) {
ABThread abThread = new ABThread();
BAThread baThread = new BAThread();
abThread.start();
baThread.start();
}
}
// 向访问共享变量,在访问打印机,将对共享变量计算的结果发送到打印机
class ABThread extends Thread {
@Override
public void run() {
// 首先,尝试访问共享变量
synchronized (Demo1.lockA) {
try {
Thread.sleep(2000);
System.out.println("ABThread 加 lockA锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 访问共享变量做计算,得到一些结果
synchronized (Demo1.lockB) {
// 把共享变量计算的结果发送到打印机来打印
System.out.println("ABThread 加 lockB锁");
}
}
}
}
// 先获取打印机的访问权,在尝试访问共享变量,将共享变量计算结果发送到打印机打印
class BAThread extends Thread {
@Override
public void run() {
// 首先,尝试访问打印机
synchronized (Demo1.lockA) {
// 获取打印机的访问权
try {
Thread.sleep(2000);
System.out.println("BAThread 加 lockB锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Demo1.lockB) {
//访问共享变量, 计算出结果,把结果发送到打印机打印
System.out.println("BAThread 加 lockA锁");
}
}
}
}
运行结果:
解决方案二的代码实现:
public class Demo1 {
static Object lockA = new Object();
static Object lockB = new Object();
static Object allLock=new Object();
public static void main(String[] args) {
ABThread abThread = new ABThread();
BAThread baThread = new BAThread();
abThread.start();
baThread.start();
}
}
// 向访问共享变量,在访问打印机,将对共享变量计算的结果发送到打印机
class ABThread extends Thread {
@Override
public void run() {
synchronized(Demo1.allLock){
// 首先,尝试访问共享变量
synchronized (Demo1.lockA) {
try {
Thread.sleep(100);
System.out.println("ABThread 加 lockA锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 访问共享变量做计算,得到一些结果
synchronized (Demo1.lockB) {
// 把共享变量计算的结果发送到打印机来打印
System.out.println("ABThread 加 lockB锁");
}
}
}
}
}
// 先获取打印机的访问权,在尝试访问共享变量,将共享变量计算结果发送到打印机打印
class BAThread extends Thread {
public void run() {
synchronized (Demo1.allLock) {
// 首先,尝试访问打印机
synchronized (Demo1.lockB) {
// 获取打印机的访问权
try {
Thread.sleep(2000);
System.out.println("BAThread 加 lockB锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Demo1.lockA) {
//访问共享变量, 计算出结果,把结果发送到打印机打印
System.out.println("BAThread 加 lockA锁");
}
}
}
}
}
运行结果截图: