上文讲到synchronized关键字在多线程中的使用,既然用到了锁,就会有出现死锁的情况。一个线程获得锁,如果其他线程也想获得同样的锁就会阻塞住,等待锁的释放。如果线程A已经获得锁1,还要获得锁2,同时线程B已经获得锁2,还要获得锁1,那么线程A和B就会一直阻塞住。
例子
依照惯例先举个例子:
public class Test {
public static void main(String[] args) throws InterruptedException {
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(new Test().new Tt1(lock1, lock2));
Thread t2 = new Thread(new Test().new Tt2(lock1, lock2));
t1.start();
t2.start();
}
class Tt1 implements Runnable{
private Object lock1;
private Object lock2;
public Tt1(Object lock1,Object lock2) {
this.lock1 = lock1;
this.lock2 = lock2;
}
@Override
public void run() {
synchronized (lock1) {
System.out.println(this.getClass()+"-------1");
try {
Thread.sleep(1000);
synchronized (lock2) {
System.out.println(this.getClass()+"-------2");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Tt2 implements Runnable{
private Object lock1;
private Object lock2;
public Tt2(Object lock1,Object lock2) {
this.lock1 = lock1;
this.lock2 = lock2;
}
@Override
public void run() {
synchronized (lock2) {
System.out.println(this.getClass()+"-------1");
try {
Thread.sleep(1000);
synchronized (lock1) {
System.out.println(this.getClass()+"-------2");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
执行结果:
class Test$Tt1-------1
class Test$Tt2-------1
2个线程都只执行到了第一步,就没有往下执行,都处于了阻塞状态,这就是死锁。
定位死锁
死锁问题并不是一个容易被发现和定位的问题,如果系统出现死锁问题,该如何定位?
1.使用jps,查询java虚拟机的pid
2.使用jstack打印堆栈
以Thread-1为例介绍下每一部分的意思
1).Thread-1 线程名称
2).prio=5 线程优先级
3).os_prio=0 本地的优先级
4).tid=0x000000001929b800 线程id
5).nid=0xfb34 本地的线程id
6)
java.lang.Thread.State: BLOCKED (on object monitor)
线程状态处于阻塞
7) waiting to lock <0x00000000d5dd0c38> (a java.lang.Object)正在等待的锁
8)locked <0x00000000d5dd0c48> (a java.lang.Object) 已获取的锁
现在我们可以看到Thread-1和Thread-0都处于阻塞状态,Thread-1获取了‘0x00000000d5dd0c48’锁,等待‘0x00000000d5dd0c38’锁,而Thread-0则刚好相反。
避免死锁
我们已经知道了死锁的形成和定位,再来讲讲如何避免:
1.尽量在线程中不嵌套获取多个资源,但你只需获取一个时就不会出现死锁、
2.如果不得不嵌套使用时,要多考虑嵌套的顺序
3.死锁是因为无限的阻塞等待,如果能有一个最大的等待时间就可以解决这个问题。synchronized不具备这个功能,但是我们可以使用Lock类中的tryLock方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后返回失败信息
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。