当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么他永远被阻塞,当线程A持有锁L并想获得锁M的同时,线程B持有锁M并同时尝试获得锁L时,那么两个线程将永远的等待下去,这中情况就是简单的死锁的形式,其中多个线程由于存在环路的锁依赖关系而永远的等待下去,那么就存在一个死锁。

1、锁顺序死锁

  下面是顺序锁的一个列子,代码如下:

 package deadLock;

 public class LeftRightDeadLock {
private final Object left = new Object();
private final Object right = new Object(); public void leftRight() throws Exception{
synchronized (left) {
Thread.sleep(2000);
synchronized (right) {
System.out.println("left to right");
}
}
} public void rightLeft() throws Exception{
synchronized (right) {
Thread.sleep(2000);
synchronized (left) {
System.out.println("right to left");
}
}
} }
 package deadLock;

 public class LeftRightThread extends Thread {

     private LeftRightDeadLock d;
public LeftRightThread(LeftRightDeadLock d){
this.d = d;
}
@Override
public void run() {
try{
d.leftRight();
}catch(Exception ex){
ex.printStackTrace();
}
} }
 package deadLock;

 public class RightLeftThread extends Thread {

     private LeftRightDeadLock d;
public RightLeftThread(LeftRightDeadLock d){
this.d = d;
}
@Override
public void run() {
try{
d.rightLeft();
}catch(Exception ex){
ex.printStackTrace();
}
} }
 package deadLock;

 public class Main {
public static void main(String[] args) {
LeftRightDeadLock d = new LeftRightDeadLock();
LeftRightThread t1 = new LeftRightThread(d);
RightLeftThread t2 = new RightLeftThread(d);
t1.start();
t2.start();
}
}

线程t1持有left的锁,并尝试获取得right的锁,而线程t2持有right的锁,并尝试获得left的锁,故产生死锁。产生死锁的原因是:两个线程试图以不同的顺序来获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也就不会产生死锁。

  如果所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁的问题

2、动态的锁顺序死锁

  有时候,并不能清楚的知道是否在锁顺序上有足够的控制权来避免死锁的发生,看如下转账的代码。

  

 public class TransferAccounts {
public void transferMoney(Account fromAccount, Account toAccount, double amount) throws Exception{
synchronized (fromAccount) {
synchronized (toAccount) {
if(fromAccount.getBalance() - amount < 0){
throw new Exception();
}
else{
fromAccount.setBalance(amount);
toAccount.add(amount);
}
}
}
}
}
 public class Account {

     //金额
private double balance; public double getBalance() {
return balance;
} public void setBalance(double balance) {
this.balance = balance;
}
public void add(double amount){
balance += amount;
}
public void subtra(double amount){
balance -= amount;
} }

  上面代码是资金从一个账户转到另一个账户的简单实现,在开始转账之前要获得这两个Account对象的锁,以确保通过原子方式来更新两个账户的中余额。所有的线程似乎都是按照顺序锁来获得锁,但是事实上锁的顺序取决于转给函数transferMoney参数顺序,而这些参数又取决于外部的输入,如果两个线程同时调用transferMoney,其中一个线程从X向Y转账,另一个线程从Y向X转账,有可能发生死锁:

  线程A:transferMoney(xAccount, yAccount);

  线程B:transferMoney(yAccount, xAccount);

  为了防止这种情况发生,必须按照顺序来获取锁。如下代码:

 public class TransferAccounts {
private static final Object tieLock = new Object(); public void transfer(Account fromAccount, Account toAccount,
double amount) throws Exception {
if (fromAccount.getBalance() - amount < 0) {
throw new Exception();
} else {
fromAccount.setBalance(amount);
toAccount.add(amount);
}
}
public void transferMoney(Account fromAccount, Account toAccount,
double amount) throws Exception{
int fromHash = fromAccount.hashCode();
int toHash = toAccount.hashCode();
if(fromHash < toHash){
synchronized (fromAccount) {
synchronized (toAccount) {
transfer(fromAccount, toAccount, amount);
}
}
}else if(fromHash > toHash){
synchronized (toAccount) {
synchronized (fromAccount) {
transfer(fromAccount, toAccount, amount);
}
}
}else {
synchronized (tieLock) {
synchronized (fromAccount) {
synchronized (toAccount) {
transfer(fromAccount, toAccount, amount);
}
}
}
}
} }

  在极少数的情况下,两个对象可能拥有相对的hashCode值,这时必须通过添加额外的锁来,在获得两个Account的锁之前,必须先获得这个额外的锁。从而消除死锁的发生。

3、死锁的避免与诊断

(1) 如果一个程序每次至多只能获取一个锁,那么就不会产生锁顺序死锁。

(2) 如果必须获取多个锁,那么设计时必须考虑锁的顺序,如果按照固定的顺序来获取锁,就不会发生锁顺序死锁。

(3)支持定时锁,例如显示使用Lock类中的定时tryLock功能来代替内置锁,显示锁可以指定一个超时时限,在等待超过某个时间后tryLock会返回一个失败的信息。

Java多线程——死锁的更多相关文章

  1. Java 多线程 死锁 隐性死锁 数据竞争 恶性数据竞争 错误解决深入分析 全方向举例

    在几乎所有编程语言中,由于多线程引发的错误都有着难以再现的特点,程序的死锁或其它多线程错误可能只在某些特殊的情形下才出现,或在不同的VM上运行同一个程序时错误表现不同.因此,在编写多线程程序时,事先认 ...

  2. Java 多线程 --死锁及解决方案

    在java 多线程中 过多的同步造成相互不释放资源 从而相互等待,造成死锁线现象,一般发生于同步中持有多个对象锁 如以下代码: public class DeadLock { public stati ...

  3. java多线程--死锁

    1. Java中导致死锁的原因 Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2 ...

  4. Java 多线程 - 死锁deadlock产生原因+避免方法

    ref: java中产生死锁的原因及如何避免 https://blog.csdn.net/m0_38126177/article/details/78587845 java如何避免死锁 http:// ...

  5. Java多线程死锁的产生实例

    死锁产生的四个必要条件: (1) 互斥条件:一个资源每次只能被一个进程使用.(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放.(3) 不剥夺条件:进程已获得的资源,在末使用完 ...

  6. java 多线程死锁

    死锁案例: package com.test; public class DealThread implements Runnable { public String username; public ...

  7. java多线程死锁

    进程(线程)同步的基本概念 进程之间的制约关系 1. 直接制约关系(进程同步) 这个关系主要源于进程合作,例如,有一个输入进程A通过单缓冲向进程B提供数据,当该缓冲空时,进程B因为不能获得所需数据而被 ...

  8. Java多线程编程核心技术(二)对象及变量的并发访问

    本文主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题.阅读本文应该着重掌握如下技术点: synchronized对象监视器为O ...

  9. Java多线程中的死锁问题

    Java程序基本都要涉及到多线程,而在多线程环境中不可避免的要遇到线程死锁的问题.Java不像数据库那么能够检测到死锁,然后进行处理,Java中的死锁问题,只能通过程序员自己写代码时避免引入死锁的可能 ...

随机推荐

  1. DataGrid组件

    <?xml version="1.0" encoding="utf-8"?><s:WindowedApplication xmlns:fx=& ...

  2. Netty学习第六节实例一步学习

    NIO与传统IO对应使用的类: ServerSocketChannel相当于ServerSocket SocketChannel 相当于Socket Selector是NIO的核心类,是负责监听Ser ...

  3. Duplicate entry '1' for key 'PRIMARY'

    这个bug产生在你建立主键的时候,就是说主键的id重复了,有两个同名的id,需要删除一个,才能满足主键的唯一性

  4. java获取变量的类型

    对于简单类型变量,是无法直接获得变量类型的://要想获取,必须自定义函数进行返回 对于包装类型变量,是可以直接获得的,变量名称.getClass().getName(); java中获取变量的类型可以 ...

  5. LdA笔记

    LDA算法最初的论文使用的是变分EM方法训练(Variational Inference).该方法较为复杂,而且最后训练出的topic主题非全局最优分布,而是局部最优分布.后期发明了Collapsed ...

  6. SPSS-两变量相关性分析

    两个变量之间存在确定性:关系和不确定关系(会存在一定的波动范围),就好比你的亲生母亲绝对只有一个,而你的亲叔叔可能有好几个(可以在1叔—4叔之间波动) 相关性一般分为   1:强正相关关系  (一个值 ...

  7. Codeforces801B Valued Keys 2017-04-19 00:21 136人阅读 评论(0) 收藏

    B. Valued Keys time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...

  8. Dreamweaver杀手!Illustrator终结者?Flash的末日?图形图像设计程序之网页版

    Dreamweaver杀手!Illustrator终结者?Flash的末日?图形图像设计程序之网页版 阅读:  评论:  作者:Rybby  日期:  来源:rybby.com Adobe 家的 Il ...

  9. [jquery-delegate] iphone_4s _iphone _5c_中不兼容jQuery delegate 事件(does not wok)

    1. jQuery .on() and .delegate() doesn't work on iPad http://stackoverflow.com/questions/10165141/jqu ...

  10. delphi 连接oracle对接代码

    selectt4.vclen60 as pat_d_codename,t4.vclen60 as pat_sexname,t1.pat_sex,t1.pat_d_code,t1.pat_in_no,t ...