死锁
锁顺序死锁
当两个线程以不同的顺序来获得相同的锁
public class A {
private final Object left = new Object();
private final Object right = new Object();
public void leftRight() {
synchronized (left) {
synchronized (right) {
}
}
}
public void rightLeft() {
synchronized (right) {
synchronized (left) {
}
}
}
}

动态的锁顺序死锁
class User {
private int money;
public int getMoney() {
return money;
}
public User(int money) {
this.money = money;
}
public void add(int value) {
money += value;
}
public void sub(int value) {
money -= value;
}
}
对于transferMoney(),虽然只有一个加锁顺序,但上锁的顺序取决于参数的顺序,test()在调用时仍可能会导致死锁
public class A {
public void transferMoney(User from, User to, int money) {
synchronized (from) {
synchronized (to) {
if (from.getMoney() < money) {
throw new IllegalStateException();
} else {
from.sub(money);
to.add(money);
}
}
}
}
public void test() {
User A = new User(1000);
User B = new User(1000);
transferMoney(A, B, 10);
transferMoney(B, A, 20);
}
}
要解决上面问题必须定义锁的顺序,如下使用HashCode定义锁的顺序,当HashCode一样时,引入一个额外的锁(如果对象有唯一、不可变且可比较的域,可以对其排序省去额外的锁)
public class A {
private static final Object tieLock = new Object();
public void transferMoney(User from, User to, int money) {
class Helper {
public void transfer() {
if (from.getMoney() < money) {
throw new IllegalStateException();
} else {
from.sub(money);
to.add(money);
}
}
}
int fromHash = System.identityHashCode(from);
int toHash = System.identityHashCode(to);
if (fromHash < toHash) {
synchronized (from) {
synchronized (to) {
new Helper().transfer();
}
}
} else if (fromHash > toHash) {
synchronized (to) {
synchronized (from) {
new Helper().transfer();
}
}
} else {
synchronized (tieLock) {
synchronized (from) {
synchronized (to) {
new Helper().transfer();
}
}
}
}
}
public void test() {
User A = new User(1000);
User B = new User(1000);
transferMoney(A, B, 10);
transferMoney(B, A, 20);
}
}
死锁的避免与诊断
支持定时的锁
当使用内置锁时,只要没有获得锁就会永远等待,而显示锁的tryLock可以设置等待时间
通过线程转储信息来分析死锁
Thread Dump包括各个运行中的线程的栈追踪信息、加锁信息
在生成线程转储之前,JVM将在等待关系图中通过搜索循环来找出死锁
其他活跃性危险
饥饿
当线程由于无法访问它所需要的资源(如CPU时钟周期)而不能继续执行时,称为饥饿
Thread API定义了10个线程优先级,JVM会将它们映射到操作系统的调度优先级,应该避免修改优先级,因为其可能导致低优先级的线程发生饥饿
活锁
线程不断重复执行相同的失败操作,如在处理事务时发生错误,回滚后继续再次提交,解决办法是将不可修复的错误进行回退
当多个相互协作的线程都对彼此进行响应从而修改各自的状态,使得都无法继续执行(如两个人面对面走左边,又同时换到右边),解决办法是等待随机时间后再修改状态

被折叠的 条评论
为什么被折叠?



