Java多线程——避免活跃性危险

死锁

锁顺序死锁

当两个线程以不同的顺序来获得相同的锁

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会将它们映射到操作系统的调度优先级,应该避免修改优先级,因为其可能导致低优先级的线程发生饥饿

活锁

线程不断重复执行相同的失败操作,如在处理事务时发生错误,回滚后继续再次提交,解决办法是将不可修复的错误进行回退

当多个相互协作的线程都对彼此进行响应从而修改各自的状态,使得都无法继续执行(如两个人面对面走左边,又同时换到右边),解决办法是等待随机时间后再修改状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值