死锁
线程等待资源,形成一个环路就会造死锁。
数据库中事务也可能造成死锁,但是事务会自动选择一个杀死,保证另外的运行,线程可没有这么自动
锁顺序死锁
直接上代码
这种死锁,只要保持锁的顺序就不会发生
public class TestCallable {
private Object left = new Object();
private Object right = new Object();
public void left(){
synchronized (left){
synchronized (right){
// dosomething
}
}
}
public void right(){
synchronized (right){
synchronized (left){
// dosomething
}
}
}
}
动态锁顺序死锁
如下,因为不能保证传入参数的顺序性:
public class TestCallable {
public void left(Object left, Object right){
synchronized (left){
synchronized (right){
// dosomething
}
}
}
}
如下可以避免死锁:
public class TestCallable {
public void left(Object left, Object right){
if(System.identityHashCode(left) > System.identityHashCode(right)){
synchronized (left){
synchronized (right){
// dosomething
}
}
}else if(System.identityHashCode(right) > System.identityHashCode(left)){
synchronized (right){
synchronized (left){
// dosomething
}
}
}else{
synchronized (this){
// dosomething
}
}
}
}
另外还有可能协作对象间的死锁。这个就防不胜防了。具体问题需要具体分析。不过其实大多数情况下是没有必要给程序加锁的,也就不会出现问题。
资源等待死锁
线程A用到两个数据库连接,持有一个了,不释放,又去拿另外一个,结果池里面没有了,就会造成死锁。
或者是线程A的运行结束依赖于池中B线程,B线程等待A执行结束才会开始执行,就会造成死锁。
避免死锁
定时锁
用显示锁的tryLock来替代内部锁,超时无法获得锁后会抛出异常,重新交回控制权
其他活跃度危险
饥饿
通常是因为CPU的资源不够用了,有一个线程的优先级比较低,总有比他高优先级的在运行,就轮不到他。这种问题现在应该基本不会见到了。
活锁
尽管不会阻塞,但是任然不能执行,因为其会重复相同的操作,永远忙碌状态。
比如一个线程执行失败,然后被加到队尾,然后又执行,又失败。