避免活跃性

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秒钟后重试,那么它们又会发生冲突,并且不断地冲突下去,因而即使有大量闲置的带宽,也无法使数据包发送出去。为了避免这种情况发生,需要让它们分别等待一段随机的时间。

原文地址:https://www.cnblogs.com/domi22/p/8538439.html

时间: 2024-11-25 16:27:35

避免活跃性的相关文章

《Java并发编程实战》第十章 避免活跃性危急 读书笔记

一.死锁 所谓死锁: 是指两个或两个以上的进程在运行过程中.因争夺资源而造成的一种互相等待的现象.若无外力作用.它们都将无法推进下去. 百科百科 当两个以上的运算单元,两方都在等待对方停止执行,以取得系统资源,可是没有一方提前退出时,这样的状况,就称为死锁.维基百科 1. 顺序死锁 最少有两个锁.一个线程获取到A锁须要获取B锁才干进行操作,而另外一个线程获取到了B锁.须要获取A锁才干运行操作.这样的情况下easy出现顺序死锁. public class LeftRightDeadlock { p

第10章避免活跃性危险

如果每个需要锁L和锁M的线程都以相同的顺序来获取L和M,那么就不会发生死锁了. 解决这个问题,必须定义锁的顺序,并在整个应用程序中都按照这个顺序来获取锁. 在制定锁的顺序时,可以使用System.identityHashCode方法,该方法将返回由Object.hashCode返回的值. private static final Object tieLock = new Object(); public void transferMoney(final Account fromAcct, fin

java并发编程实战学习笔记之第三部分:活跃性、性能与测试

第十章 避免活跃性危险 锁顺序死锁:定义锁的顺序,可以通过某种方法决定每个锁的顺序,比如hashcode或者序列号之类的 在锁的调用顺序不是很明显的情况下,在持有锁的情况下调用其他外部方法一定要注意,可以通过开放调用,避免发生死锁的危险,即使用同步代码块保护仅仅保护那些共享变量即可,但这种降低锁粒度的方法可能会使得原本大的代码块失去原子性,解决办法为:将服务的状态改为关闭之前一直持有锁,状态改变之后,其他线程也就能够看到关闭信息从而不会再次执行关闭操作... 死锁的诊断与避免:    通过try

《Java并发编程实战》第十章 避免活跃性危险 读书笔记

一.死锁 所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去. 百科百科 当两个以上的运算单元,双方都在等待对方停止运行,以取得系统资源,但是没有一方提前退出时,这种状况,就称为死锁.维基百科 1. 顺序死锁 最少有两个锁,一个线程获取到A锁需要获取B锁才能进行操作,而另外一个线程获取到了B锁,需要获取A锁才能执行操作,这种情况下容易出现顺序死锁. public class LeftRightDeadlock { priva

java并发编程(3)避免活跃性危险

活跃性危险 一.死锁 发生:每个人都不愿意放弃自己的锁,确想要别人的锁,这就会导致死锁  1.锁顺序死锁:如果每个线程以固定的顺序获取锁,那么至少在程序中不会出现锁顺序导致的死锁: 因为顺序固定如:所有线程:A-B-C 则无问题,如果一个A-B B-A则会发生死锁 例子1:简单死锁 public class LeftRightDeadlock { private final Object left = new Object(); private final Object right = new

网易Java研发面试官眼中的Java并发——安全性、活跃性、性能

一. 安全性问题 线程安全的本质是正确性,而正确性的含义是程序按照预期执行 理论上线程安全的程序,应该要避免出现可见性问题(CPU缓存).原子性问题(线程切换)和有序性问题(编译优化) 需要分析是否存在线程安全问题的场景:存在共享数据且数据会发生变化,即有多个线程会同时读写同一个数据 针对该理论的解决方案:不共享数据,采用线程本地存储(Thread Local Storage,TLS):不变模式 Ⅰ. 数据竞争 数据竞争(Data Race):多个线程同时访问同一数据,并且至少有一个线程会写这个

【Java并发基础】安全性、活跃性与性能问题

前言 Java的多线程是一把双刃剑,使用好它可以使我们的程序更高效,但是出现并发问题时,我们的程序将会变得非常糟糕.并发编程中需要注意三方面的问题,分别是安全性.活跃性和性能问题. 安全性问题 我们经常说这个方法是线程安全的.这个类是线程安全的,那么到底该怎么理解线程安全呢? 要给线程安全性定一个非常明确的定义是比较复杂的.越正式的定义越复杂,也就越难理解.但是不管怎样,在线程安全性定义中,最核心的概念还是正确性,可以简单的理解为程序按照我们期望的执行. 正确性的含义是:某个类的行为与其规范完全

【并发编程】活跃性问题

来自为知笔记(Wiz) 附件列表

第10章 避免活跃性危险

1.死锁 检测死锁:在等待关系的有向图中搜索循环. 产生死锁的4个必要条件: 1)互斥条件:共享资源独占访问 2)不可剥夺条件:不能强制其他线程释放资源 3)请求和保持条件:在等待申请的新的资源时,继续占有已分配的资源 4)循环等待条件:发生死锁时,存在一个循环等待的队列{P1, P2, P3, ...} P1等待P2占有的资源,P2等待P3占有的资源,...,Pn等待P1占有的资源. 死锁的预防: 1.资源一次性分配 2.当某进程新申请的资源未获取时,释放已获取的资源 3.资源有序分配,给资源