【Java并发编程】之九:死锁(含代码)

当线程需要同时持有多个锁时,有可能产生死锁。考虑如下情形:

线程A当前持有互斥所锁lock1,线程B当前持有互斥锁lock2。接下来,当线程A仍然持有lock1时,它试图获取lock2,因为线程B正持有lock2,因此线程A会阻塞等待线程B对lock2的释放。如果此时线程B在持有lock2的时候,也在试图获取lock1,因为线程A正持有lock1,因此线程B会阻塞等待A对lock1的释放。二者都在等待对方所持有锁的释放,而二者却又都没释放自己所持有的锁,这时二者便会一直阻塞下去。这种情形称为死锁。

下面给出一个两个线程间产生死锁的示例,如下:

[java] view
plain
copy

  1. public class Deadlock extends Object {
  2. private String objID;
  3. public Deadlock(String id) {
  4. objID = id;
  5. }
  6. public synchronized void checkOther(Deadlock other) {
  7. print("entering checkOther()");
  8. try { Thread.sleep(2000); }
  9. catch ( InterruptedException x ) { }
  10. print("in checkOther() - about to " + "invoke ‘other.action()‘");
  11. //调用other对象的action方法,由于该方法是同步方法,因此会试图获取other对象的对象锁
  12. other.action();
  13. print("leaving checkOther()");
  14. }
  15. public synchronized void action() {
  16. print("entering action()");
  17. try { Thread.sleep(500); }
  18. catch ( InterruptedException x ) { }
  19. print("leaving action()");
  20. }
  21. public void print(String msg) {
  22. threadPrint("objID=" + objID + " - " + msg);
  23. }
  24. public static void threadPrint(String msg) {
  25. String threadName = Thread.currentThread().getName();
  26. System.out.println(threadName + ": " + msg);
  27. }
  28. public static void main(String[] args) {
  29. final Deadlock obj1 = new Deadlock("obj1");
  30. final Deadlock obj2 = new Deadlock("obj2");
  31. Runnable runA = new Runnable() {
  32. public void run() {
  33. obj1.checkOther(obj2);
  34. }
  35. };
  36. Thread threadA = new Thread(runA, "threadA");
  37. threadA.start();
  38. try { Thread.sleep(200); }
  39. catch ( InterruptedException x ) { }
  40. Runnable runB = new Runnable() {
  41. public void run() {
  42. obj2.checkOther(obj1);
  43. }
  44. };
  45. Thread threadB = new Thread(runB, "threadB");
  46. threadB.start();
  47. try { Thread.sleep(5000); }
  48. catch ( InterruptedException x ) { }
  49. threadPrint("finished sleeping");
  50. threadPrint("about to interrupt() threadA");
  51. threadA.interrupt();
  52. try { Thread.sleep(1000); }
  53. catch ( InterruptedException x ) { }
  54. threadPrint("about to interrupt() threadB");
  55. threadB.interrupt();
  56. try { Thread.sleep(1000); }
  57. catch ( InterruptedException x ) { }
  58. threadPrint("did that break the deadlock?");
  59. }
  60. }

运行结果如下:

从结果中可以看出,在执行到other.action()时,由于两个线程都在试图获取对方的锁,但对方都没有释放自己的锁,因而便产生了死锁,在主线程中试图中断两个线程,但都无果。

大部分代码并不容易产生死锁,死锁可能在代码中隐藏相当长的时间,等待不常见的条件地发生,但即使是很小的概率,一旦发生,便可能造成毁灭性的破坏。避免死锁是一件困难的事,遵循以下原则有助于规避死锁:

1、只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法;

2、尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂;

3、创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁;

时间: 2024-10-19 01:50:18

【Java并发编程】之九:死锁(含代码)的相关文章

java并发编程(九)死锁

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17200937 大部分代码并不容易产生死锁,死锁可能在代码中隐藏相当长的时间,等待不常见的条件地发生,但即使是很小的概率,一旦发生,便可能造成毁灭性的破坏.避免死锁是一件困难的事,遵循以下原则有助于规避死锁: 1.只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法: 2.尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂: 3.创建和使

Java并发编程(九)安全发布

之前讨论是如何将对象封闭在线程之中,这样可以减少一些并发带来的同步和可见性问题.但是在有些时候,我们希望在多个线程间共享对象,此时必须确保安全地进行共享. [不安全发布的示例] 可见性问题:其他线程看到的是不一致的状态:对象尚未创建完成就发布出去了. 不正确的发布:正确的对象被破坏 上被完全创建的对象还没有完整性,此时发布出去将使其他线程看到的是不一致的状态,然后看到对象的状态突然发生变化,即使线程在对象发布后还没有修改过它. [示例代码] 未正确发布对象可能有两种情况: 1. 其他线程看到的对

java并发编程04:死锁

1.什么是死锁 多线程以及多进程改善了系统资源的利用率并提高了系统的处理能力.然而,并发执行也带来了新的问题--死锁. 所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进. 2.死锁产出的必要条件 以下这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁. 2.1 互斥条件 线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个线程所占有.若此时其他线程请求该资源,则请求线

Java并发编程(九)线程间协作(下)

上篇我们讲了使用wait()和notify()使线程间实现合作,这种方式很直接也很灵活,但是使用之前需要获取对象的锁,notify()调用的次数如果小于等待线程的数量就会导致有的线程会一直等待下去.这篇我们讲多线程间接协作的方式,阻塞队列和管道通讯,间接协作的优点是使用起来更简单并且不易出错. 阻塞队列 阻塞队列提供了一种功能,即你可以在任何时刻向队列内扔一个对象,如果队列满了则当前线程阻塞:在任何时刻都可以从队列中取出一个对象,如果队列为空则当前线程阻塞.阻塞队列是线程安全的,使用它时无需加锁

Java并发编程(8):多线程环境中安全使用集合API(含代码)

Java并发编程(8):多线程环境中安全使用集合API(含代码)JAVA大数据中高级架构 2018-11-09 14:44:47在集合API中,最初设计的Vector和Hashtable是多线程安全的.例如:对于Vector来说,用来添加和删除元素的方法是同步的.如果只有一个线程与Vector的实例交互,那么,要求获取和释放对象锁便是一种浪费,另外在不必要的时候如果滥用同步化,也有可能会带来死锁.因此,对于更改集合内容的方法,没有一个是同步化的.集合本质上是非多线程安全的,当多个线程与集合交互时

[Java并发编程实战]构建一个高效可复用缓存程序(含代码)

[Java并发编程实战]构建一个高效可复用缓存程序(含代码) 原文地址:https://www.cnblogs.com/chengpeng15/p/9915800.html

转: 【Java并发编程】之二十:并发新特性—Lock锁和条件变量(含代码)

简单使用Lock锁 Java5中引入了新的锁机制--Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接口有3个实现它的类:ReentrantLock.ReetrantReadWriteLock.ReadLock和ReetrantReadWriteLock.WriteLock,即重入锁.读锁和写锁.lock必须被显式地创建.锁定和释放,为了可以使用更多的功能,一般用ReentrantLock为其实例化

转: 【Java并发编程】之十八:第五篇中volatile意外问题的正确分析解答(含代码)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17382679 在<Java并发编程学习笔记之五:volatile变量修饰符-意料之外的问题>一文中遗留了一个问题,就是volatile只修饰了missedIt变量,而没修饰value变量,但是在线程读取value的值的时候,也读到的是最新的数据.但是在网上查了很多资料都无果,看来很多人对volatile的规则并不是太清晰,或者说只停留在很表面的层次,一知半解. 这两天看<深入Ja

Java 并发编程之任务取消(九)

Jvm关闭 jvm可正常关闭也可强行关闭,正常关闭有多种触发方式: 当最后一个正常(非守护,下面会讲到什么是守护线程)线程结束时 当调用system.exit时,或者通过其他特定于平台的方法关闭时(例如发送了SIGINT信号或键入Ctrl-c) 通过其他特定平台的方法关闭jvm,调用Runtime.halt或者在操作系统当中杀死JVM进程(例如发送sigkill)来强行关闭jvm. 关闭钩子 在正常关闭中,jvm首先调用所有已注册的关闭钩子,关闭钩子是指通过 Runtime.addShutdow

《Java并发编程实战》要点笔记及java.util.concurrent 的结构介绍

买了<java并发编程实战>这本书,看了好几遍都不是很懂,这个还是要在实战中找取其中的要点的,后面看到一篇文章笔记做的很不错分享给大家!! 原文地址:http://blog.csdn.net/cdl2008sky/article/details/26377433 Subsections  1.线程安全(Thread safety) 2.锁(lock) 3.共享对象 4.对象组合 5.基础构建模块 6.任务执行 7.取消和关闭 8.线程池的使用 9.性能与可伸缩性 10.并发程序的测试 11.显