当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么他永远被阻塞,当线程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. event对象的理解

    0.给对象绑定事件准确的说是给对象事件绑定事件函数 1.event:事件对象,当一个事件发生的时候,和当前这个对象发生的事件有关的信息都会被i临时保存到event对象中 2.event对象必须在一个事 ...

  2. hdu2571 命运 2016-09-11 16:54 53人阅读 评论(0) 收藏

    命运 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  3. Ajax请求php返回json对象数据中包含有数字索引和字符串索引,在for in循环中取出数据的顺序问题

    //php中的数组数据格式 Array ( [all] => Array ( [title] => ALL [room_promotion_id] => all ) [best_av ...

  4. Oracle sql 优化の索引监控

    1.监视索引是否使用 除了主键是完整性约束而自动变为索引外,创建普通索引的目的就是为了提高查询速度,如果我们创建了索引而没有被使用,那么这些不被使用的索引将起到阻碍性能的作用. 语法: --检查某个索 ...

  5. Chrome For EBS

    https://chrome.google.com/webstore/detail/oracle-ebs-r12-enablement/ekkagabmggbmpmncofhgkfigmeldifnc ...

  6. 今天犯了一个StringBuilder构造函数引起的二逼问题。

    在.Net里,StringBuilder的构造函数有很多,最常用的是无参的构造函数,默认分配16个字符的空间.其次就是填写StringBuilder空间的带一个Int32的构造函数,这个在优化代码的时 ...

  7. Backup--还原选项之STANDBY

    很多DBA对还原时制定RECOVERY 与 NORECOVERY选项都很熟悉,但是对于STANDBY就有点茫然了,今天一起来学习下吧. --============================== ...

  8. python——排序

    从学校毕业出来后只知道冒泡排序,发现自己对排序的了解还是很浅显. 于是在网上搜索各种排序方法,以下是本人根据索搜出来的资料再结合自己理解作出的一些简单的阐述. 如果有不正确的地方欢迎大家指正.(共同学 ...

  9. C# OleDbConnection对特定部分Excel的数据读取

    最近在写winform程序,先来一个简单的. 读取特定部分Excel的数据读取,读取Excel第30行开始到H列的数据 using System;using System.Collections.Ge ...

  10. iOS App的加固保护原理

    本文由  网易云发布. 本文从攻防原理层面解析了iOS APP的安全策略.iOS以高安全性著称,但它并非金刚不坏之身.对于信息安全而言,止大风于青萍之末是上上策,杭研深入各个细节的研发工作,正是网易产 ...