当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么他永远被阻塞,当线程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. 更新vs2017 15.9.2后,在指定-T v141_xp情况下载编译会报下面warning MSB8051

    更新vs2017 15.9.2后,在指定-T v141_xp情况下载编译会报下面warning: C:\Program Files (x86)\Microsoft Visual Studio\2017 ...

  2. jquery统计显示或隐藏的元素个数

    统计显示的checkbox的数量: 统计隐藏的checkbox数量:

  3. flask_模板

    由于python中生成html比较繁琐,所以flask自动为你配置好jinjia2模板.下面我们开始学习模板应用吧~ 1.编写microblog模块 注:(1)这里为了渲染模板,我们从Flask导入了 ...

  4. UVa 12034 Race (递推+组合数学)

    题意:A,B两个人比赛,名次有三种情况(并列第一,AB,BA).输入n,求n个人比赛时最后名次的可能数. 析:本来以为是数学题,排列组合,后来怎么想也不对.原来这是一个递推... 设n个人时答案为f( ...

  5. java使用WebUploader做大文件的分块和断点续传

    版权所有 2009-2018荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/up6.2/in ...

  6. post同步请求

    // http://api.hudong.com/iphonexml.do?type=focus-c     //post请求中url不带请求参数,请求参数在参数HTTPBody中设置, 需要创建可变 ...

  7. 《Android开发艺术探索》第11章 Android的线程和线程池

    第11章 Android的线程和线程池 11.1 主线程和子线程 (1)在Java中默认情况下一个进程只有一个线程,也就是主线程,其他线程都是子线程,也叫工作线程.Android中的主线程主要处理和界 ...

  8. linux 通过md5查找重复文件

    代码如下: md5sum *|sort |uniq -w32 -D|awk -F ' ' '{print $2}' uniq 部分参数 -c #在每行前显示该行重复次数. -d #只输出重复的行. - ...

  9. leanCloud 笔记

    目的:javascript实时通讯.感觉:nodejs的socket.io加了一个图形界面和接口,它保证了所有环境下的实时通信. 最新版leancloud支持的服务:实时消息推送,实时点对点消息服务. ...

  10. 团队作业(HCL队)第三周—需求改进和系统分析

    2.需求&原型改进: 1.问题:游戏中坦克的移动和攻击怎么控制的? 改进: 在游戏中,我控制我方坦克,按下方向键坦克便向按下的方向移动,按下Z键,我方坦克发射炮弹.敌方坦克面向随机的方向移动, ...