一文彻底理解线程死锁

1.线程死锁的原因

当多条线程以不同的顺序抢占同步资源的时候,就有可能发生死锁。

如下图所示,线程1持有锁对象A而希望获得锁对象B;另一方面,线程2持有锁对象B而希望获得锁对象A。并且这两个线程的操作是交错执行的,因此它们会发生死锁。

当发生的死锁后,JDK自带了两个工具(jstack和JConsole),可以用来监测分析死锁的发生原因。

jstack工具用于生于生成虚拟机当前时刻的线程快照。线程快照就是当前虚拟机每一条线程正在执行的方法堆栈的集合,生成快照可以用于定位诸如线程死锁、死循环等问题。

JConsole是一种可视化监视管理工具。用于连接正在运行的JVM进程,以监控 Java 应用程序性能和跟踪 Java 中的代码。

2.死锁示例

下面以一个死锁例子来说明如何使用这两个工具来分析线程死锁。

class SynThread implements Runnable{
	
	int a ,b;
	
	public SynThread(int a,int b){
		this.a=a;
		this.b=b;
	}
	@Override
	public void run() {
		synchronized (Integer.valueOf(a)) {//必须用valueOf()方法
			synchronized (Integer.valueOf(b)) {
				System.err.println("a+b=="+(a+b));
			}
		}
	}
	
}

public class entry {

	public static void main(String[] args) {
		
		//循环主要是为了加大死锁概率
		for(int i=0;i<100;i++){
			new Thread(new SynThread(1,2)).start();
			new Thread(new SynThread(2,1)).start();
		}
	}
}

说明:以上代码有可能发生死锁,原因是Integer.valueOf()方法作了缓存优化,对[-128,127]之间的数字会被缓存。也就是说,循环代码中一共只创建了两个不同的对象。假设在两个synchronized块之间发生了线程切换,那就有可能造成,线程A等待被线程B持有Integer.valueOf(1)对象,线程B等待被线程A持有Integer.valueOf(2)对象,结果出现了死锁。(可能需要多次执行直到程序出现阻塞现象)

JDK源代码Integer.java类对valueOf()方法的优化细节如下

    public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }

3.死锁检测方法

3.1使用jstack工具检查死锁(以Windows环境为例)

1.找到运行当前程序的JVM的进程id,命令及结果如下



2.运行jstack命令,并将结果信息导出来

3.使用文本编辑器打开刚导出的文本,只要查看最后关于死锁的堆栈信息即可

4.直接从堆栈信息不能直观得到结论,没关系,我们可以画图理清线程间的调度情况(线程41持有b10对象,希望获得b00对象;线程56持有b00对象,希望获得b10对象

3.2使用JConsole可视化工具检测死锁

1.直接执行JConsole工具(在jdk/bin目录下),选择目标JVM进程,然后点击连接

2.切换到“线程”标签页,点击“检测死锁”按钮

3.选择不同的线程,可以查看其资源调度信息,采用类似的方法分析,可以得出与采用jstack分析一样的结论

4.如何避免死锁

死锁的本质是:多条线程以不同的访问顺序获得临界资源

因此,我们只需要保证多条线程以一致的访问顺序来获得临界资源即可。

例如,银行转账业务,用户A对用户B进行转账。由于用户B同一时刻也有可能向用户A进行转账。

我们只需要保证,不管有多少条不同的线程在处理转账业务,永远以一致的访问顺序。例如按身份证的号码进行排序,先锁住用户A再锁住用户B(当然,还有其他排序策略)。

Python面向对象编程(Object-Oriented Programming,简称OOP)是一种程序设计方法,它将数据和操作数据的方法组合成对象,通过定义类(class)来创建对象。下面是一些概念和原则,可以帮助你更好地理解Python面向对象编程。 1. 类和对象: - 类是一种抽象的数据类型,它定义了对象的属性和方法。 - 对象是类的实例,它具有类定义的属性和方法。 2. 属性和方法: - 属性是对象的数据,可以是整数、字符串、列表等。 - 方法是对象的行为,可以是函数或过程。 3. 封装: - 封装是将数据和对数据的操作封装在一起,以创建一个独立的实体。 - 使用类来封装数据和方法,可以隐藏实现细节,提高代码的可读性和可维护性。 4. 继承: - 继承是一种机制,允许一个类继承另一个类的属性和方法。 - 子类可以重用父类的代码,并且可以添加新的属性和方法。 5. 多态: - 多态是指同一个方法可以在不同的类中具有不同的实现方式。 - 多态可以提高代码的灵活性和可扩展性。 下面是一个简单的例子,展示了如何定义一个类、创建对象并调用对象的方法: ```python class Person: def __init__(self, name, age): self.name = name self.age = age def say_hello(self): print(f"Hello, my name is {self.name} and I'm {self.age} years old.") # 创建对象 person = Person("Alice", 25) # 调用对象的方法 person.say_hello() ``` 这个例子定义了一个名为`Person`的类,它有两个属性(`name`和`age`)和一个方法(`say_hello`)。我们通过`Person`类创建了一个名为`person`的对象,并调用了它的`say_hello`方法。 希望这个简单的例子能帮助你更好地理解Python面向对象编程。如果你有其他问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jforgame

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值