<高级-1> 并发活跃性问题

[b]一、避免活跃性危险[/b]
活跃性没有明确的定义。安全性的含义是“永远不发生糟糕的事情”,而活跃性则关注于另一个目标,即“某件正确的事情[b]最终会发生[/b]”。当某个操作无法继续执行下去时,就会发生活跃性问题。在串行程序中,活跃性问题的形式之一就是无意中造成的[b]无限循环[/b],从而使循环之后的代码无法得到执行。线程将带来其他一些活跃性问题。例如,如果线程 A在等待线程B释放其持有的资源,而线程B永远都不释放该资源,那么A就会永久地等待下去。本次将介绍各种形式的活跃性问题,以及如何避免这些问题, 包括[b]死锁,饥饿,以及活锁。[/b]
[b]在安全性与活跃性之间通常存在着某种制衡[/b]。我们使用加锁机制来确保线程安全,但如果过度地使用加锁,则可能导致因锁的顺序死锁(Lock-Ording Deadlock),这通常是因为需要获得两个及以上的锁时发生。同样,我们使用线程池和信号量来限制对资源的使用,但这些限制的行为可能会导致资源死锁(Resource Deadlock)。 Java 应用程序无法从死锁中恢复过来,因此在设计时一定要排除那些可能导致死锁的条件。

[b]1.1 死锁[/b]
经典的“哲学家进餐”问题很好的描述了死锁情况。有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到了这就会造成死锁了。
由上面也可以看出是因为每个哲学家需要获取两个共享资源,而且存在环路依赖关系才导致的死锁。

[b]1.1.1 Lock-ording deadlock[/b]
[img]http://dl2.iteye.com/upload/attachment/0095/9254/d9e58465-da84-3d0b-a6ad-11dddba54d36.bmp[/img]

两个线程试图以不同的顺序来获得相同的锁。如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也不会产生死锁。再看看哲学家问题,正是这种情况。
想要验证锁顺序的一致性,需要对程序中的加锁行为进行全局分析。单独分析每条获取多个锁的路径是不够的,因为单独看每个获取锁的方式看起来都是“合理”的。
[img]http://dl2.iteye.com/upload/attachment/0095/9258/e0695ad8-2567-3fde-af5d-12d038c0da58.bmp[/img]

但有时候并不能清楚地知道是否在锁顺序上有足够的控制权来避免死锁的发生。看如下转账的代码:
[img]http://dl2.iteye.com/upload/attachment/0095/9260/e7e32ed5-d63d-3a63-a9d1-4c26589719f5.bmp[/img]

所有的线程似乎都是按照相同的顺序获得锁,但事实上锁的顺序取决于传递给加锁方法的参数顺序,而这些参数顺序又取决于外部输入,如果一个线程从X向Y转账,另一个线程从Y向X转账,就会发生死锁。
[color=red][b]解决此问题必须定义锁的顺序[/b][/color],并在整个应用程序中都按照这个顺序来获取锁。制定锁的顺序时,要按照一个稳定的排序方式来排,比如通过System.identityHashCode方法,返回Object.hashCode值来作为排序的比较。
[img]http://dl2.iteye.com/upload/attachment/0095/9264/300082a3-d754-3cdc-9dec-a911ca3dfa54.bmp[/img]
[img]http://dl2.iteye.com/upload/attachment/0095/9266/908b6e2d-1e87-337e-87d1-775f9a1241b9.bmp[/img]

上面例子使用了加时锁,虽然增加了代价,但保证了安全性,而且使用加时锁的几率也非常低。如果排序时能获得唯一的键值进行排序那就更容易了,比如银行账号就可以用来作为唯一的键值,不需要加时锁。

[b]1.1.2 在协作对象之间发生的死锁[/b]
某些获取多个锁的操作不像上面那么容易判断,获取两个锁的操作并不一定必须在一个方法中被获取。如A类的同步方法里使用了B类的同步方法,B类的同步方法也使用了A类的同步方法,那么一个线程使用A类,一个线程使用B类,也会出现交叉等待,[b]协同死锁[/b]。

[b]1.1.3 开放调用[/b]
上面的情况,A类和B类并不知道他们会陷入死锁,而且他们本来也不应该知道。方法调用相当于一种抽象屏障,因而你无需了解在被调用方法中所执行的操作。也正是因为这种屏障,所以也难以分析可能出现的死锁。

如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用(Open Call).可以通过开放调用来避免死锁的发生,类似于采用封装机制来提供线程安全的的方法。

[b]1.1.4 资源死锁[/b]
正如当多个线程相互持有彼此正在等待的锁而又不释放自己已持有的锁时会发生死锁。当他们在相同的资源集合上等待时,也会发生死锁。

[b]1.2 死锁的避免与诊断[/b]
如果一个程序每次至多只获得一个锁,那么就不会产生lock-ording deadlock,但这并不现实。在使用细粒度的锁的过程中,可以通过使用一种两阶段策略来检查代码中的死锁:
[color=red][b](1)[/b][/color]首先找出在什么地方将获取多个锁(使这个集合尽可能小),[color=red][b]
(2)[/b][/color]然后对所有这些实例进行全局分析,从而确保他们在整个程序中获取锁的顺序都保持一致。尽可能使用开放调用。

[b]1.2.1 支持定时的锁[/b]
这一项技术可以检测死锁和从死锁中恢复过来,即显式使用Lock类中的定时[b]tryLock[/b]功能来代替内置锁机制。

[b]1.2.2 通过线程转储信息来分析死锁[/b]
Jvm可以通过线程转储(Thread Dump)来帮助识别死锁的发生。

[b]1.3 其他活跃性危险[/b]
尽管死锁是最常见的活跃性危险,但在并发程序中还存在一些其他的活跃性危险,包括饥饿,丢失信号,活锁等。
[b]饥饿:[/b]线程由于无法访问它所需要的资源而不能继续执行。最常见的资源就是CPU时钟周期,如优先级低的线程由于其他高优先级线程一直执行导致无法获得cpu时间。
[b]活锁:[/b]该问题不会阻塞线程,但也不能继续执行,因为线程不断重复执行相同的操作,而且总会失败。当多个相互协作的线程对彼此进行退让,修改各自的状态,但反复避让,导致活锁。就像两个相遇的人互相让路交叉等待。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值