java线程研究---(10)Thread同步:死锁
原创
©著作权归作者所有:来自51CTO博客作者茜茜770的原创作品,请联系作者获取转载授权,否则将追究法律责任
死锁
synchronized块的乱用,会造成死锁。
下面来看看死锁形成的大致情况:
- 线程T1,执行线程对象A
- 线程T2,执行线程对象B
- 线程对象A,里面有加锁的代码需要获得自己的锁,并且同时有另外一个加锁的代码需要获得线程对象B的锁。
- 线程对象B,里面有加锁的代码需要获得自己的锁,并且同时有另外一个加锁的代码需要获得线程对象A的锁。
- 简言之:
- 线程对象A在获得自己的锁的时候,需要获得线程对象B的锁
- 线程对象B在获得自己的锁的时候,需要获得线程对象A的锁
- 那么这两个对象都在占用自己的锁的时候,想要获得对方对象的锁。
- 因此,这两个对象都在等锁的池(lock pool)中,都处于等待拿锁的状态。
- 所以死锁就形成了——两个对象会无限的等待下去。。。
来看看死锁示例代码:
DeadThreadA.java
package thread;
public class DeadThreadA implements Runnable {
public DeadThreadB deadThreadB; // 想拿到对方线程对象的锁,就得先获得引用
private int i = 0;
private int j = 0;
@Override
public void run() {
while (true) {
synchronized (this) { // 首先占用自己对象的锁
if (i >= 10)
break;
i++;
for (int j = 0; j < 10000000l; j++)
;
System.out.println(Thread.currentThread().getName() + ": i=" + i);
while (true) {
synchronized (deadThreadB) { // 在占用自己对象的锁的同时,需要获得deadThreadB的锁
if (j >= 10) {
break;
}
j++;
for (int j = 0; j < 10000000l; j++)
;
System.out.println(Thread.currentThread().getName() + ": j=" + j);
}
}
}
}
}
}
DeadThreadB.java
package thread;
public class DeadThreadB implements Runnable {
public DeadThreadA deadThreadA; // 想拿到对方线程对象的锁,就得先获得引用
private int i = 0;
private int j = 0;
@Override
public void run() {
while (true) {
synchronized (this) { // 首先占用自己对象的锁
if (i >= 10)
break;
i++;
for (int j = 0; j < 10000000l; j++)
;
System.out.println(Thread.currentThread().getName() + ": i= " + i);
while (true) {
synchronized (deadThreadA) { // 在占用自己对象的锁的同时,需要获得deadThreadA的锁
if (j >= 10) {
break;
}
j++;
for (int j = 0; j < 10000000l; j++)
;
System.out.println(Thread.currentThread().getName() + ": j= " + j);
}
}
}
}
}
}
代码的几点说明:
- synchronized()块,只支持传入对象,如果想拿对方线程对象的锁,那么首先得获得对方线程对象的引用。
- 当前的两个 线程对象,首先是各自已经占用了自己的锁,同时再,都尝试去拿对方的锁。
- 所以,先synchronized (this),在占用自己的锁的同时,(没有释放自己的锁)再去拿对方的锁:synchronized (deadThreadB), synchronized (deadThreadA)
- 也就是说,当前线程,要同时拿两个锁:首先是拿到自己锁,然后再去尝试拿对方的锁。
执行类,如下
DeadThreadMain.java
package thread;
public class DeadThreadMain {
public static void main(String ab[]) {
DeadThreadA a = new DeadThreadA();
DeadThreadB b = new DeadThreadB();
b.deadThreadA = a; // 获得对方线程对象的引用
a.deadThreadB = b; // 获得对方线程对象的引用
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
t1.start();
t2.start();
}
}
控制台就打印这么多,之后程序是一直运行的,但是就像死循环一样,永不停止了:
Thread-1: i= 1
Thread-0: i=1
根据示例代码,来具体讲一下死锁过程:
- t1线程启动的时候,首先占用了a对象的锁
- t2线程启动的时候,首先占用了b对象的锁
- t1线程占用了a对象的锁的同时,尝试拿b对象的锁:b对象的锁,一直在t2线程手里
- t2线程占用了b对象的锁的同时,尝试拿a对象的锁:a对象的锁,一直在t1线程手里
- t1和t2线程都处于等待拿对方的锁的状态,
- 死锁产生