Java多线程——死锁
当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么他永远被阻塞,当线程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多线程——死锁的更多相关文章
- Java 多线程 死锁 隐性死锁 数据竞争 恶性数据竞争 错误解决深入分析 全方向举例
在几乎所有编程语言中,由于多线程引发的错误都有着难以再现的特点,程序的死锁或其它多线程错误可能只在某些特殊的情形下才出现,或在不同的VM上运行同一个程序时错误表现不同.因此,在编写多线程程序时,事先认 ...
- Java 多线程 --死锁及解决方案
在java 多线程中 过多的同步造成相互不释放资源 从而相互等待,造成死锁线现象,一般发生于同步中持有多个对象锁 如以下代码: public class DeadLock { public stati ...
- java多线程--死锁
1. Java中导致死锁的原因 Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2 ...
- Java 多线程 - 死锁deadlock产生原因+避免方法
ref: java中产生死锁的原因及如何避免 https://blog.csdn.net/m0_38126177/article/details/78587845 java如何避免死锁 http:// ...
- Java多线程死锁的产生实例
死锁产生的四个必要条件: (1) 互斥条件:一个资源每次只能被一个进程使用.(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放.(3) 不剥夺条件:进程已获得的资源,在末使用完 ...
- java 多线程死锁
死锁案例: package com.test; public class DealThread implements Runnable { public String username; public ...
- java多线程死锁
进程(线程)同步的基本概念 进程之间的制约关系 1. 直接制约关系(进程同步) 这个关系主要源于进程合作,例如,有一个输入进程A通过单缓冲向进程B提供数据,当该缓冲空时,进程B因为不能获得所需数据而被 ...
- Java多线程编程核心技术(二)对象及变量的并发访问
本文主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题.阅读本文应该着重掌握如下技术点: synchronized对象监视器为O ...
- Java多线程中的死锁问题
Java程序基本都要涉及到多线程,而在多线程环境中不可避免的要遇到线程死锁的问题.Java不像数据库那么能够检测到死锁,然后进行处理,Java中的死锁问题,只能通过程序员自己写代码时避免引入死锁的可能 ...
随机推荐
- 改变yii2 $form最外层div样式
<?php $form = ActiveForm::begin([ 'options'=>['class' => 'form-horizontal row-border','enct ...
- Shell 基本语法
一. Linux基本命令 1.1. cp命令 该命令的功能是将给出的文件或目录拷贝到另一文件或目录中,功能十分强大. 语法: cp [选项] 源文件或目录 目标文件或目录 1.2. mv命令 用户可 ...
- UVa 10382 Watering Grass (区间覆盖贪心问题+数学)
题意:有一块长为l,宽为w的草地,在其中心线有n个喷水装置,每个装置可喷出以p为中心以r为半径的圆, 选择尽量少的装置,把草地全部润湿. 析:我个去啊,做的真恶心,看起来很简单,实际上有n多个坑啊,首 ...
- java中的类、对象、方法
类=一个种类(class)东西 对象=属于该种类的一个对象/物件(object,台湾翻译为‘物件’)方法=对这个种类的东西都可以进行的操作 比如我有一辆汽车-类 public class car {. ...
- python 求最大子序列
动态规划的本质,是对问题状态的定义和状态转移方程的定义.dynamic programming is a method for solving a complex problem by breaki ...
- [unchecked] 对作 为原始类型Hashtable的成员的put(K,V)的调用未经过检查。。。
问题: C:\Users\Administrator\Desktop\java\SoundApplet.java:212: 警告: [unchecked] 对作为原始类型Hashtable的成员的pu ...
- 基于MOD13A1的锡林郭勒草原近13年植被覆盖变化 分析
内蒙古师范大学地理科学学院 内蒙古师范大学遥感与地理信息系统重点实验室 摘要:本研究以内蒙古锡林郭勒草原为研究区,基于MOD13A1遥感数据,经过遥感预处理,得到研究区2001-2013年共13年夏季 ...
- TFlearn——(1)notMNIST
1, 数据集简介 notMNIST, 看名字就知道,跟MNIST脱不了干系,其实就是升级版的MNIST,含有 A-J 10个类别的艺术印刷体字符,字符的形状各异,噪声更多,难度比 MNIST 要 ...
- hdu 5009 离散化
http://acm.hdu.edu.cn/showproblem.php?pid=5009 有一段序列,涂连续一段子序列的代价为该子序列出现不同数字个数的平方,求最小代价涂完整个序列. ai有10^ ...
- 20、docker swarm
Swarm是Docker官方提供的一款集群管理工具,其主要作用是把若干台Docker主机抽象为一个整体,并且通过一个入口统一管理这些Docker主机上的各种Docker资源.Swarm和Kuber ...