上文讲到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方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后返回失败信息


中华雪碧
204 声望7 粉丝