摘要:上一章介绍过关键字,使用它可以给程序互斥部分加上一把锁从而达到同步的效果,但错误的用法会导致多个线程同时被阻塞死锁死锁多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
死锁上一章介绍过synchronized关键字,使用它可以给程序互斥部分加上一把锁从而达到同步的效果,但错误的用法会导致多个线程同时被阻塞....
死锁: 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
JAVA 中死锁产生的四个必要条件
互斥使用,当资源被一个线程使用(占有)时,别的线程不能使用
不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
请求和保持,当资源请求在请求其他的资源的同时保持对原有资源的占有。
循环等待,存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
经典案例先来看一段经典的死锁代码,两个方法中分别获取了class literals方式的锁
void method1() { while (true) { synchronized (Integer.class) { System.out.println("获得 Integer.class 对象锁"); synchronized (String.class) { System.out.println("获得 String.class 对象锁"); } } } } void method2() { while (true) { synchronized (String.class) { System.out.println("获得 String.class 对象锁"); synchronized (Integer.class) { System.out.println("获得 Integer.class 对象锁"); } } } } public static void main(String[] args) { DeadLockFixed fixed = new DeadLockFixed(); new Thread(fixed::method1, "method1").start(); new Thread(fixed::method2, "method2").start(); } ////////////////////////日志//////////////////////// //获得 Integer.class 对象锁 //获得 String.class 对象锁 //获得 Integer.class 对象锁 //获得 String.class 对象锁 //获得 Integer.class 对象锁 //获得 String.class 对象锁 //获得 Integer.class 对象锁 //获得 String.class 对象锁 //获得 Integer.class 对象锁 //获得 String.class 对象锁 ////////////////////////日志////////////////////////
分析: method1 和 method2 都会去获取Integer和String两把锁,但是获取锁的顺序不一致,在并行情况,当两个方法未及时释放自己的锁,又同时去获取另外一把锁这时候就会出现死锁情况
举个例子: 吃饭需要碗和筷子,小明吃饭的时候先拿筷子在拿碗,小红相反,再一次拿取餐具的时候,小明先拿到筷子,正准备拿碗的时候被小红抢先了一步拿碗,这时候都要吃饭但谁也不让谁,结果就是都饿死(死锁)
分析手段命令: JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps、jstack、jmap、jhat、jstat等小巧的命令工具,本章主要使用到jps与jstack做分析
jps: 过滤出Java本身的进程以及运行的引导类,就是引导main方法所在的类。
-q 仅输出VM标识符,不包括class name,jar name,arguments in main method -m 输出main method的参数 -l 输出完全的包名,应用主类名,jar的完全路径名 -v 输出jvm参数 -V 输出通过flag文件传递到JVM中的参数(.hotspotrc文件或-XX:Flags=所指定的文件 -Joption 传递参数到vm,例如:-J-Xms48m
jstack: 主要用来查看某个Java进程内的线程堆栈信息
-l long listings,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况 -m mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)
打开dos界面,输入jps
找到当前main的pid,使用jstack pid命令
我们可以发现method1锁住了<0x000000076ae03e68>等待<0x000000076ae008d8>,method2恰恰相反,这个时候死锁就发生了...
日志滑动到后面,可以发现有一个Found one Java-level deadlock,然后也告诉了我们代码在多少行,在知道问题代码所在地后,解决起来就简单了(改变锁的顺序,完美解决问题...)
补充两句上面讲了什么是死锁以及定位问题方式,其实JDK中,已经内置了很多锁相关的类都在java.util.concurrent.locks包下,使用简单且可以有效的的避免死锁问题,或者使用java.util.concurrent.Semaphore(信号量)机制,篇幅过长会导致阅读兴趣下降,所以信号量处理锁的方式在GIT中,有兴趣可以从GIT地址获取
- 说点什么全文代码:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter6
个人QQ:1837307557
battcn开源群(适合新手):391619659
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/67628.html
摘要:如果有其它线程调用了相同对象的方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,从新争夺锁的拥有权。 wait,notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视,而本文则是对这些关键字的使用进行描述。 存在即合理 在java中,每个对象都有两个池,锁池(monitor)和等待池(waitset),每个...
摘要:每个对象只有一个锁与之相关联。实现同步则是以系统开销作为代价,甚至可能造成死锁,所以尽量避免滥用。这种机制确保了同一时刻该类实例,所有声明为的函数中只有一个方法处于可执行状态,从而有效避免了类成员变量访问冲突。 synchronized是JAVA语言的一个关键字,使用 synchronized 来修饰方法或代码块的时候,能够保证多个线程中最多只有一个线程执行该段代码 ... 概述 ...
摘要:在之前,不能为线程单独设置或指定一个默认的,为了设置,需要继承并覆写方法。幸运的是后线程提供了一个方法,用来捕获并处理因线程中抛出的未知异常,以避免程序终止。 在单线程的开发过程中,通常采用try-catch的方式进行异常捕获,但是这种方式在多线程环境中会显得无能为力,而且还有可能导致一些问题的出现,比如发生异常的时候不能及时回收系统资源,或者无法及时关闭当前的连接... 概述 Ja...
摘要:在退出时执行必要的挽救措施。在这种情况下,一旦被提供,等待一个进程终止指定的时间。如果进程在该时间限制内没有终止,则通过发出或中的对等方强制终止进程。所以有可能这是在中途执行时发生的。 shutdownHook是一种特殊的结构,它允许开发人员插入JVM关闭时执行的一段代码。这种情况在我们需要做特殊清理操作的情况下很有用 用途 在Jboss,Jetty等容器中都可以看到shutdown...
摘要:我的是忙碌的一年,从年初备战实习春招,年三十都在死磕源码,三月份经历了阿里五次面试,四月顺利收到实习。因为我心理很清楚,我的目标是阿里。所以在收到阿里之后的那晚,我重新规划了接下来的学习计划,将我的短期目标更新成拿下阿里转正。 我的2017是忙碌的一年,从年初备战实习春招,年三十都在死磕JDK源码,三月份经历了阿里五次面试,四月顺利收到实习offer。然后五月怀着忐忑的心情开始了蚂蚁金...
阅读 902·2021-11-22 13:53
阅读 2532·2021-10-15 09:40
阅读 999·2021-10-14 09:42
阅读 3474·2021-09-22 15:59
阅读 887·2021-09-02 09:47
阅读 2365·2019-08-30 15:54
阅读 1437·2019-08-29 17:14
阅读 398·2019-08-29 15:15