励精图治---Concurrency---吞吐量、活跃性及死锁

更新概念

所谓吞吐量跟活跃性:就是单位时间内,系统处理信息的数量,速度表现的结果。

所谓死锁:就是两个线程,我有你要的,你有我要的,互不相让,都等对方让出来,就死锁了。


分析情景

锁顺序死锁:所有的线程申请锁的顺序不一样,那么就存在一定概率发生死锁。

换言之,如果所有线程都以 相同的顺序来获得锁,那么在程序中就不会出现锁顺序死锁问题。


危害

死锁,小应用当然没事。如果你是商业应用的软件呢。比如淘宝,特么一个锁一天可能要上百亿次被执行。要命了这


看范例1

public class EasyDead {
    private final Object mALock = new Object();
    private final Ojbect mBLock = new Object();
    
    public void wantB(){
        synchronized(mALock) {
            synchronized(mBLock) {
                doSomethings();
            }
        }
    }
    
    public void wantA(){
        synchronized(mBLock) {
            synchronized(mALock) {
                doSomethings();
            }
        }
    }
}

在多线程情况下,同时有线程执行了wantA跟wangB,那么就会导致死锁。


看范例2---百度上有说的银行转账面试题

public void trans(Account fromAccount, Account toAccount, int money) {
    synchronized(fromAccount) {
        synchronized(toAccount) {
            if (fromAccount.getMoney() < money) {
                throws new Exception();
            } else {
                fromAccount.del(money);
                toAccount.add(money);
            }
        }
    }
}

这里如果有两个账户,同时进行存钱和取钱操作。。。那么不就卡住了。看起来不会卡。其实依旧会卡。这个环路可以弄的大一点。

这两个范例,一个是静态直观可以发现。一个是动态的。

皆因锁顺序引起。而这里的加锁顺序还是可以改变的。这并不影响


那么静态是可以弄成一样的。动态的话,就需要用一定的规则:比如说hashCode。利用System.identityHashCode来计算。按照计算后的值排序。虽然依旧有一定的概率会一样。但毕竟是小概率事件。锁顺序死锁+小概率事件===极小概率事件。大幅降低也不错。

修改修改,看范例3

private final Object mLock = new Object();

public void trans(Account fromAccount, Account toAccount, int money) {
    int from = System.identityHashCode(fromAccount);
    int to   = System.identityHashCode(toAccount);
    if(from > to) {
        synchronized(fromAccount) {
            synchronized(toAccount) {
                if (fromAccount.getMoney() < money) {
                    throws new Exception();
                } else {
                    fromAccount.del(money);
                    toAccount.add(money);
                }
            }
        }
    } else if(from < to) {
        synchronized(toAccount) {
            synchronized(fromAccount) {
                if (fromAccount.getMoney() < money) {
                    throws new Exception();
                } else {
                    fromAccount.del(money);
                    toAccount.add(money);
                }
            }
        }
    } else {
        synchronized(mLock) {//#多加一个私有锁。里头的锁顺序就不要紧了
            synchronized(toAccount) {
                synchronized(fromAccount) {
                    if (fromAccount.getMoney() < money) {
                        throws new Exception();
                    } else {
                        fromAccount.del(money);
                        toAccount.add(money);
                    }
                }
            }
        }
    }
}

这里排个序,然后针对一样的情况,再额外加个锁,吞吐也不会有大的影响。


范例4:对象间悄悄藏起来的死锁。

class A {
    private final B b;
    public A (B b) {
        this.b = b;
    }
    
    public synchronized void tellB_toDoSomething() {
        this.b.doB();
    }
    public synchronized void doA() {
        
    }
}


class B {
    private A a;
    public B(){
        a = new A(this);
    }
    public synchronized void telA_toDoSomething() {
        this.a.doA();
    }
    public synchronized void doB() {
        
    }
}

在对象间的死锁,A让B做事,A先获取自己的锁,然后获取B的锁;此时B也让A做事,B先获取自己的锁,在获取A的锁。自然又是死锁了。

这种情况也是锁顺序导致的。就是藏的比较深。


资源死锁

之前说,如果Executors之间有线程依赖的。那么,我们会扩大线程池也需要慎重考虑超时策略。

这里就存在一个,线程饥饿等待的情况。就是资源死锁了。

另外,在线程池满的情况下,所有新任务进入等待状态,如果一个线程需要两个资源,另一个线程也需要这两个资源,也存在死锁。


解决策略

1.超时。设置锁超时,超时就释放

2.通过dump log分析,我们的程序应该都是有追踪的嘛。上传log到服务器。我们好分析

3.轮询策略。但是这很可能会损耗CPU资源。不断的访问。这终究是个下策。

4.设置线程优先级,Thread Priority还是很有意思的。有了优先级。会有改善。

5.轮询策略的失败会导致活锁

活锁,就是其实也是锁,就是不断的访问,不会完全锁死。但是这会消耗很大的资源。每次失败后都会重试。----------策略。每次失败后的重试时间随机化


小结

死锁的存在不仅降低了稳定性。也影响的吞吐量活性。要谨慎。死锁一般是又加锁顺序不一致导致的。一旦不一致就会产生求之而不得,不得则等待的情况。也就死锁了。

不到一定有需要尽量不要使用锁。开放调用更易进行死锁分析。

同时,如果要使用锁,那么要尽量减少锁代码块。越少越好。动不动就保护一整块不合适。





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值