`
byandby
  • 浏览: 1695755 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java 死锁例子及相关讲解

阅读更多
死锁

  死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

  导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性的访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。

  由于这个原因,在使用“synchronized”关键词时,很容易出现两个线程互相等待对方做出某个动作的情形。代码一是一个导致死锁的简单例子。

//代码一
class Deadlocker {
 int field_1;
 private Object lock_1 = new int[1];
 int field_2;
 private Object lock_2 = new int[1];

 public void method1(int value) {
  “synchronized” (lock_1) {
   “synchronized” (lock_2) {
    field_1 = 0; field_2 = 0;
   }
  }
 }

 public void method2(int value) {
  “synchronized” (lock_2) {
   “synchronized” (lock_1) {
    field_1 = 0; field_2 = 0;
   }
  }
 }
}
 


  参考代码一,考虑下面的过程:

  ◆ 一个线程(ThreadA)调用method1()。

  ◆ ThreadA在lock_1上同步,但允许被抢先执行。

  ◆ 另一个线程(ThreadB)开始执行。

  ◆ ThreadB调用method2()。

  ◆ ThreadB获得lock_2,继续执行,企图获得lock_1。但ThreadB不能获得lock_1,因为ThreadA占有lock_1。

  ◆ 现在,ThreadB阻塞,因为它在等待ThreadA释放lock_1。

  ◆ 现在轮到ThreadA继续执行。ThreadA试图获得lock_2,但不能成功,因为lock_2已经被ThreadB占有了。

  ◆ ThreadA和ThreadB都被阻塞,程序死锁。

  当然,大多数的死锁不会这么显而易见,需要仔细分析代码才能看出,对于规模较大的多线程程序来说尤其如此。好的线程分析工具,例如JProbe Threadalyzer能够分析死锁并指出产生问题的代码位置。

  隐性死锁

  隐性死锁由于不规范的编程方式引起,但不一定每次测试运行时都会出现程序死锁的情形。由于这个原因,一些隐性死锁可能要到应用正式发布之后才会被发现,因此它的危害性比普通死锁更大。下面介绍两种导致隐性死锁的情况:加锁次序和占有并等待。

  加锁次序

  当多个并发的线程分别试图同时占有两个锁时,会出现加锁次序冲突的情形。如果一个线程占有了另一个线程必需的锁,就有可能出现死锁。考虑下面的情形,ThreadA和ThreadB两个线程分别需要同时拥有lock_1、lock_2两个锁,加锁过程可能如下:

  ◆ ThreadA获得lock_1;

  ◆ ThreadA被抢占,VM调度程序转到ThreadB;

  ◆ ThreadB获得lock_2;

  ◆ ThreadB被抢占,VM调度程序转到ThreadA;

  ◆ ThreadA试图获得lock_2,但lock_2被ThreadB占有,所以ThreadA阻塞;

  ◆ 调度程序转到ThreadB;

  ◆ ThreadB试图获得lock_1,但lock_1被ThreadA占有,所以ThreadB阻塞;

  ◆ ThreadA和ThreadB死锁。

  必须指出的是,在代码丝毫不做变动的情况下,有些时候上述死锁过程不会出现,VM调度程序可能让其中一个线程同时获得lock_1和lock_2两个锁,即线程获取两个锁的过程没有被中断。在这种情形下,常规的死锁检测很难确定错误所在。

  占有并等待

  如果一个线程获得了一个锁之后还要等待来自另一个线程的通知,可能出现另一种隐性死锁,考虑代码二。

//代码二
public class queue {
 static java.lang.Object queueLock_;
 Producer producer_;
 Consumer consumer_;

 public class Producer {
  void produce() {
   while (!done) {
    “synchronized” (queueLock_) {
     produceItemAndAddItToQueue();
     “synchronized” (consumer_) {
      consumer_.notify();
     }
    }
   }
  }

  public class Consumer {
   consume() {
    while (!done) {
     “synchronized” (queueLock_) {
      “synchronized” (consumer_) {
       consumer_.wait();
      }
      removeItemFromQueueAndProcessIt();
     }
    }
   }
  }
 }
}
 



  在代码二中,Producer向队列加入一项新的内容后通知Consumer,以便它处理新的内容。问题在于,Consumer可能保持加在队列上的锁,阻止Producer访问队列,甚至在Consumer等待Producer的通知时也会继续保持锁。这样,由于Producer不能向队列添加新的内容,而Consumer却在等待Producer加入新内容的通知,结果就导致了死锁。

  在等待时占有的锁是一种隐性的死锁,这是因为事情可能按照比较理想的情况发展—Producer线程不需要被Consumer占据的锁。尽管如此,除非有绝对可靠的理由肯定Producer线程永远不需要该锁,否则这种编程方式仍是不安全的。有时“占有并等待”还可能引发一连串的线程等待,例如,线程A占有线程B需要的锁并等待,而线程B又占有线程C需要的锁并等待等。

  要改正代码二的错误,只需修改Consumer类,把wait()移出“synchronized”()即可。

  因此避免死锁的一个通用的经验法则是:当几个线程都要访问共享资源A、B、C时,保证使每个线程都按照同样的顺序去访问它们,比如都先访问A,在访问B和C。
  此外,Thread类的suspend()方法也很容易导致死锁,因此这个方法已经被废弃了.


分享到:
评论

相关推荐

    JAVA_线程同步与死锁

    一个详细讲解JAVA_线程同步与死锁的例子 希望可以帮助到你。

    java多线程例子

    本文将通过三个实用的例子,讲解 Java 多线程的基本概念和使用方法。 第一个例子:创建多线程 在 Java 中,可以通过继承 Thread 类或实现 Runnable 接口来创建多线程。在第一个例子中,我们将继承 Thread 类来创建...

    JAVA5.0新特性讲解与例子

    Java 5.0,也被称为Java 1.5,是Java发展历程中的一个重要里程碑,它引入了许多创新特性,显著提升了代码的可读性、安全性和效率。以下是对这些新特性的详细讲解: 1. **泛型(Generics)** 泛型是Java 5.0最显著...

    Java应用教程(PPT&code)

    1. **Java基础知识**:讲解Java的历史、特点以及它在软件开发中的作用。介绍Java的安装、配置环境变量,并讲解如何编写第一个"Hello, World!"程序。 2. **语法结构**:深入探讨Java的语法,如变量、数据类型、...

    Thinking in Java原码例子

    1. **对象与类**:Java是一种面向对象的编程语言,"Thinking in Java"深入讲解了类、对象的创建和使用。对象是程序的基本构造块,而类则是描述对象的模板。了解如何定义类,包括属性(成员变量)和方法(成员函数)...

    java线程的例子(IBM培训提供)

    在IBM的培训中,这个主题深入讲解了如何在Java中创建和管理线程,以及如何解决并发环境中可能出现的问题。 首先,让我们来探讨一下Java中的线程创建。Java提供了两种主要方式来创建线程:通过实现`Runnable`接口和...

    java线程同步(实例讲解,清晰易懂)

    - **死锁风险**: 如果不正确地管理锁,可能会发生死锁情况。 #### 三、静态方法同步 静态方法也可以通过 `synchronized` 关键字进行同步。与实例方法不同的是,静态方法的锁是基于类而不是对象的。这意味着,对于...

    银行家算法例子超详细讲解

    银行家算法是操作系统中一种著名的避免死锁的策略,主要用于多道程序环境下资源的动态分配。这个算法模拟了银行的贷款系统,通过预先检查资源分配是否会导致系统进入不安全状态来预防死锁的发生。 在银行家算法中,...

    Java面试题大全_300个以上面试题加答案_最后附加例子

    这份"Java面试题大全"涵盖了超过300道面试题及答案,旨在帮助应聘者全面准备Java面试。 1. **基础知识**: - 讲解Java的特点,如平台独立性、面向对象、自动内存管理(垃圾回收)等。 - 阐述Java的命名规则和基本...

    Java并发编程实践高清pdf及源码

    《Java并发编程实践》是一本深入探讨Java多线程编程的经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和David Holmes等专家共同编写。这本书全面介绍了Java平台上的并发编程技术,是Java开发...

    java超强学习笔记

    6. **多线程**:Java提供了内置的多线程支持,笔记会讲解线程的创建、同步、通信等,以及死锁、活锁、饥饿等并发问题的解决方案。 7. **反射与注解**:反射允许在运行时检查和修改类、接口、字段和方法,注解可以...

    Java线程(Java.Thread)(中英版)

    "Java.Threads,3rd.Edition.chm"可能是一个关于Java线程的第三版电子手册,通常这类资源会详细讲解线程的创建、生命周期、同步机制、线程池等主题。它可能涵盖了线程的基本概念,如如何创建和启动线程,以及如何通过...

    Java Software Solutions 4th

    《Java Software Solutions 4th》是一本专注于Java软件开发的教材,主要针对快速掌握Java编程技术的读者。...通过深入阅读和实践书中的例子,读者将能够快速掌握Java软件开发,并能运用所学解决实际问题。

    Java学习利器-JAVA解惑

    10. **实战案例**:"JAVA解惑"可能包含了一些实际项目中的例子,让读者在解决问题的同时,也能看到这些知识在实际工作中的应用。 总的来说,"JAVA解惑"是一份全面且实用的Java学习资源,它不仅涵盖了理论知识,还...

    实例代码讲解JAVA多线程

    Java多线程是Java编程中的...总之,Java多线程是构建高效并发程序的关键技术,掌握好线程的创建、同步、通信及异常处理,对于提升软件性能和可维护性至关重要。通过上述两种方式创建线程,可以根据具体需求灵活选择。

    Thinking in java 电子书

    6. **多线程**:Java提供了强大的多线程支持,书中详细讲解了线程的创建、同步、中断以及死锁问题,帮助读者构建出高效、安全的并发程序。 7. **网络编程**:Java的Socket编程使得网络通信变得简单,书中涵盖了TCP...

    java线程讲解

    在本篇内容中,我们将基于提供的标题“Java线程讲解”以及描述中提及的主题来深入探讨Java中的线程概念、创建方法、线程状态及其管理等核心知识点。 ### Java线程概念 Java线程是Java语言的一个关键特性,它允许...

    Java Network Programming 4th Edition

    5. **异步I/O(AIO)**:讲解Java 7引入的异步I/O模型,如何使用AsynchronousServerSocketChannel和AsynchronousSocketChannel实现高性能的网络应用。 6. **HTTP协议与Web服务**:学习如何使用Java编写HTTP客户端和...

    java代码模板

    理解线程的创建、同步、死锁预防和并发工具的使用是成为高级Java开发者的关键。 第十四章可能涉及泛型,这是一种强大的特性,允许我们在编译时检查类型安全,并减少在编写代码时所需的类型转换。泛型在集合框架中的...

    Head First Java 中文高清版

    总的来说,《Head First Java》中文高清版不仅全面覆盖了Java语言的基础和进阶知识,而且通过生动有趣的例子和丰富的图表,使得学习过程更加愉快和高效。无论是初学者还是有一定经验的开发者,都能从中受益匪浅,...

Global site tag (gtag.js) - Google Analytics