zoukankan      html  css  js  c++  java
  • 避免活跃性

    10 避免活跃性

    在安全性与活跃性之间通常存在着某种制衡。例如加锁导致死锁,或者使用线程池和信号量来限制对资源的使用,但这些被限制的行为可能会导致资源死锁。、

    10.1 死锁

    当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞。

     

    10.1.1 锁顺序死锁

    如果用固定的顺序来获取锁,就不会发生死锁。

     

    10.1.2动态的锁顺序死锁解决方案

    其中一个线程从X向Y转账,另一个线程从Y向X转账,就会发生死锁:

    A:transferMoney(myACCOUnt,yourACCOUnt,10)

    B:transferMoney(yourACCOUnt,myACCOUnt,20)

    于我们无法控制参数的顺序,因此要解决这个问题,必须定义锁的顺序。在制定锁的顺序时,可以使用System.identityHashCode方法,该方法将返回由Object. hashCode返回的值。如果返回的hash值相同(小概率),则采用加时赛锁tieLock

     

    10.1.3开放调用

    什么是协作对象之间的死锁?

    在持有锁的情况下,调用外部(不了解)的方法,容易出现活跃性问题。在这个外部方法中可能会出现其他锁,或者长时间阻塞,导致当前持有的锁不能被其他线程获得。

    用例:

    因为setLocation和notifyAvailable都是同步方法,因此调用setL.ocation的线程将首先获取Taxi的锁,然后获取Dispatcher的锁。同样,调用getImage的线程将首先获取Dispatcher锁。然后再获取每一个Taxi的锁(每次获取一个)。产生死锁。

     

    什么是开放调用原则?

    即在调用某个方法时不需要持有锁。这样可以避免协作死锁,编码和分析安全性都变得简单

     

     

    10.1.4 资源死锁

    如果一个任务需要连接两个数据库。那么线程A可能持有与数据库D1的连接,并等待与数据库D2的连接,而线程B则持有与D2的连接并等待与D1的连接。

     

    另一种基于资源的死锁形式就是线程饥饿死锁。如果某些任务需要等待其他任务的结果,那么这些任务往往是产生线程饥饿死锁的主要来源,所以有界线程池/资源池与相互依赖的任务不能一起使用。

     

     

    10.2 死锁的避免与诊断

    10.2.1 基本方式

    A:将锁的使用顺序写入文档,避免死锁

    B:可以通过代码审查,或者借助自动化的源代码分析工具检查死锁

    C:遵循开放调用原则

     

    10.2.2 显示锁的定时功能

    使用显示锁Lock的定时功能(tryLock)代替内置锁机制。当使用内置锁时,只要没有获得锁,就会永远等待下去,而显式锁则可以指定一个超时时限(Timeout),在等待超过该时间后tryLock会返回一个失败信息。当定时锁失败时,你并不需要知道失败的原因。但是你记录了这次操作的其他有用信息。并通过一种更平缓的方式来重新启动计算,而不是关闭整个进程。

    即使在整个系统中没有始终使用定时锁,使用定时锁来获取多个锁也能有效地应对死锁问题。如果在获取锁时超时,那么可以释放这个锁,然后后退并在一段时间后再次尝试,从而消除了死锁发生的条件,使程序恢复过来。(这项技术只有在同时获取两个锁时才有效,如果在嵌套的方法调用中请求多个锁,那么即使你知道已经持有了外层的锁,也无法释放它。)

    10.2.3 线程转储

    线程转储包括各个运行中的线程的栈追踪信息,这类似于发生异常时的栈追踪信息。例如每个线程持有了哪些锁,在哪些栈帧中获得这些锁,以及被阻塞的线程正在等待获取哪一个锁。在生成线程转储之前,JVM将在等待关系图中通过搜索循环来找出死锁。

    虽然Java 6中包含对显式Lock的线程转储和死锁检测等的支持,但在这些锁上获得的信息比在内置锁上获得的信息精确度低。内置锁与获得它们所在的线程栈帧是相关联的,而显式的Lock只与获得它的线程相关联。

     

    10.3 其他活跃性危险

    10.3.1 慎用thread的优先级

    JVM根据需要将thread线程的优先级映射到操作系统的调度优先级。但是在Thread API中定义10个优先级,不同的操作系统的优先级却不同,可能少于10个,这样就造成不同优先级的线程映射为相同的优先级,使线程的优先级失去了意义。所以慎用thread优先级,尽量用Thread.sleep或Thread.yield。

     

    10.3.2糟糕的响应性

    CPU密集型的后台任务仍然可能对响应性造成影响,因为它们会与事件线程共同竞争CPU的时钟周期。

     

    10.3.3 活锁

    要解决这种活锁问题,需要在重试机制中引人随机性。

    例如,在网络上,如果两台机器尝试使用相同的载波来发送数据包,那么这些数据包就会发生冲突。这两台机器都检查到了冲突,并都在稍后再次重发。如果二者都选择了在1秒钟后重试,那么它们又会发生冲突,并且不断地冲突下去,因而即使有大量闲置的带宽,也无法使数据包发送出去。为了避免这种情况发生,需要让它们分别等待一段随机的时间。

  • 相关阅读:
    英文文法学习笔记(14)分词
    利用别名简化进入docker容器数据库的操作
    英文文法学习笔记(12)形容词
    小知识:在Exadata平台上使用ExaWatcher收集信息
    小知识:调整OCI实例的时区
    小知识:Docker环境缺少vi命令,如何解决
    小知识:Exadata平台去掉密码输错延迟10分钟登录
    英文文法学习笔记(13)副词
    SpringBoot,SpringMvc 参数校验 用法详解
    java 获取项目根路径、获取桌面路径
  • 原文地址:https://www.cnblogs.com/domi22/p/8538439.html
Copyright © 2011-2022 走看看