参考:多线程的死锁问题 很详细,建议阅读理解。
背景
Qt 项目中尝试引入 QMutex 作为成员变量保护线程安全,不小心对同一个对象连续lock两次,造成阻塞。对这个问题现象比较感兴趣,就搜索了解一下。
:对于有二次加锁风险的代码模块,加锁的时候可以尝试使用tryLock,判断其返回true再进行操作与解锁;如果判断已经加过锁了,会返回false且不会阻塞等待。
场景
第一次加锁(能够加锁成功),紧接着又遇到了代码块,再次尝试加锁。站在锁对象的视角,它认为自己已经被另外的线程给占用了,这里的第二次加锁是否要阻塞等待呢?
可重入和不可重入
线程针对同一个对象,连续加锁二次,是否会死锁
如果第二次加锁成功,这个锁就是可重入的,Java中的synchronized 是“可重入锁”;
如果第二次加锁会阻塞等待,就是不可重入的,这样的锁称为“不可重入锁”。
Java里 synchronized 和 ReentrantLock 都是“可重入锁”;C++,Python,操作系统原生的加锁API都是不可重入的.
死锁的三个典型情况
-
一个线程一把锁,连续加锁两次
如果锁是“不可重入锁”,就会死锁。
:一个线程没有释放锁, 然后又尝试再次加锁,这个锁是“不可重入锁”,第二次加锁的时候, 就会阻塞等待,直到第一次的锁被释放, 才能获取到第二个锁,但是释放第一个锁也是由该线程来完成,结果这个线程已经躺平了, 啥都不想干了, 也就无法进行解锁操作,这时候就会死锁。 -
两个线程两把锁,分别获取对方的锁
-
N个线程,M把锁