线程不安全
以转账为例,如果有两个线程同时调用transfer方法,则可能发生线程不安全问题。
void transfer(Account from, Account to, int amount) {
from.setAmout(from.getAmount - amount);
to.setAmount(to.getAmount + amount);
}
加锁导致死锁
void transfer(Account from, Account to, int amount) {
synchronized(from) {
synchronized(to) {
from.setAmout(from.getAmount - amount);
to.setAmount(to.getAmount + amount);
}
}
}
分析
在任何地方都可以线程切换,甚至是在一句语句的中间;要尽力设想对自己最不利的情况。
- 线程A执行完synchronized(from),线程B在等待from的锁;
- 线程A执行synchronized(to)前,线程B持有to的锁;
- 例如 transfer(a, b, 100)和transfer(b, a, 100)同时进行。
形成死锁的条件及如何破除
以下4点必须同时满足,才会形成死锁。防止死锁产生只需要破除其中一点。
- 互斥等待 -> 需要取消锁,一般无法破除
- hold and wait -> 一次性获取所有资源,但大部分编程语言不支持同时获取多个锁。可以设置全局锁,或者拿到第一个锁后尝试拿第二个锁,若失败则释放第一个锁,过一段时间后重试。
- 循环等待 -> 按顺序获取资源,如按ID获取锁。
- 无法剥夺的等待 -> 设置超时。