慕课网高并发实战(十)- 死锁
死锁发生的必要条件
package com.mmall.concurrency.example.deadLock;
import lombok.extern.slf4j.Slf4j;
/**
* 一个简单的死锁类
* 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
* 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
* td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
* td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
* td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
*/
// 互斥条件:o1,02排他性请求,排他性锁定,只有一个进程使用
// 请求和保持条件:请求了o1,保持了o2;另外一个是请求了o2,保存了o1
// 不剥夺条件:请求了o1后,别的资源就不能抢走他的资源
// 环路等待条件:占了o1,等待o2; 第二个是占了o2,等待o1;
@Slf4j
public class DeadLockimplements Runnable {
public int flag =1;
//静态对象是类的所有对象共享的
private static Objecto1 =new Object(), o2 =new Object();
@Override
public void run() {
log.info("flag:{}", flag);
if (flag ==1) {
synchronized (o1) {
try {
Thread.sleep(500);
}catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
log.info("run1");
log.info("1");
}
}
}
if (flag ==0) {
synchronized (o2) {
try {
Thread.sleep(500);
}catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
log.info("run2");
log.info("0");
}
}
}
}
public static void main(String[] args) {
DeadLock td1 =new DeadLock();
DeadLock td2 =new DeadLock();
td1.flag =1;
td2.flag =0;
//td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
//td2的run()可能在td1的run()之前运行
new Thread(td1).start();
new Thread(td2).start();
}
}
避免死锁条件
1 加锁顺序,对象的加锁顺序应该一样
2 加锁时间,超过时间后就放弃该锁的请求,并释放自己占用的锁
3 死锁检测
检查到死锁的操作:
1 释放所有的锁,回退,并且等待一段时间后 重试
2 设置线程优先级(随机的优先级)
10.4 HashMap 与ConcurrentHashMap
hashMap 线程不安全主要体现在:resize()会出现死循环使用迭代器的时候出现
ConcurrentModificationException,这就是所谓fail-fast策略
单线程下的扩容