摘要:如果线程已经获得锁,还要获得锁,同时线程已经获得锁,还要获得锁,那么线程和就会一直阻塞住。
上文讲到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方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后返回失败信息
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/76377.html
摘要:一个进程可以有多个线程。线程又叫做轻量级进程。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。不剥夺条件进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放只能是主动释放。 欢迎进入JAVA基础课程 博客地址:https://blog.csdn.net/houjiyu...本系列文章将主要针对JAVA一些基础知识点进...
摘要:一个进程可以有多个线程。线程又叫做轻量级进程。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。不剥夺条件进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放只能是主动释放。 欢迎进入JAVA基础课程 博客地址:https://blog.csdn.net/houjiyu...本系列文章将主要针对JAVA一些基础知识点进...
摘要:但是并不是什么多线程就可以随便用,有的时候多线程反而会造成系统的负担,而且多线程还会造成其他的数据问题,下面就来介绍一下多线程面临的问题。下面这张图是多线程运行时候的情况,我们发现上下文切换的次数暴增。 并发的概念: 在Java中是支持多线程的,多线程在有的时候可以大提高程序的速度,比如你的程序中有两个完全不同的功能操作,你可以让两个不同的线程去各自执行这两个操作,互不影响,不需要执行...
摘要:并发和并行并发和并行是两个非常容易被混淆的概念。并发说的是在一个时间段内,多件事情在这个时间段内交替执行。并行说的是多件事情在同一个时刻同事发生。由于线程池是一个线程,得不到执行,而被饿死,最终导致了程序死锁的现象。 同步(Synchronous)和异步(Asynchronous) 同步和异步通常来形容一次方法调用,同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为...
摘要:超详细的面试题总结一之基本知识多线程和虚拟机创建线程有几种不同的方式你喜欢哪一种为什么继承类实现接口应用程序可以使用框架来创建线程池实现接口。死亡线程方法执行结束,或者因异常退出了方法,则该线程结束生命周期。死亡的线程不可再次复生。 超详细的Java面试题总结(一)之Java基本知识 多线程和Java虚拟机 创建线程有几种不同的方式?你喜欢哪一种?为什么? 继承Thread类 实现R...
阅读 2033·2021-11-11 16:54
阅读 2110·2019-08-30 15:55
阅读 3610·2019-08-30 15:54
阅读 390·2019-08-30 15:44
阅读 2227·2019-08-30 10:58
阅读 423·2019-08-26 10:30
阅读 3047·2019-08-23 14:46
阅读 3190·2019-08-23 13:46