一、避免活跃性危险
活跃性没有明确的定义。安全性的含义是“永远不发生糟糕的事情”,而活跃性则关注于另一个目标,即“某件正确的事情
最终会发生”。当某个操作无法继续执行下去时,就会发生活跃性问题。在串行程序中,活跃性问题的形式之一就是无意中造成的
无限循环,从而使循环之后的代码无法得到执行。线程将带来其他一些活跃性问题。例如,如果线程 A在等待线程B释放其持有的资源,而线程B永远都不释放该资源,那么A就会永久地等待下去。本次将介绍各种形式的活跃性问题,以及如何避免这些问题, 包括
死锁,饥饿,以及活锁。
在安全性与活跃性之间通常存在着某种制衡。我们使用加锁机制来确保线程安全,但如果过度地使用加锁,则可能导致因锁的顺序死锁(Lock-Ording Deadlock),这通常是因为需要获得两个及以上的锁时发生。同样,我们使用线程池和信号量来限制对资源的使用,但这些限制的行为可能会导致资源死锁(Resource Deadlock)。 Java 应用程序无法从死锁中恢复过来,因此在设计时一定要排除那些可能导致死锁的条件。
1.1 死锁
经典的“哲学家进餐”问题很好的描述了死锁情况。有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到了这就会造成死锁了。
由上面也可以看出是因为每个哲学家需要获取两个共享资源,而且存在环路依赖关系才导致的死锁。
1.1.1 Lock-ording deadlock
两个线程试图以不同的顺序来获得相同的锁。如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也不会产生死锁。再看看哲学家问题,正是这种情况。
想要验证锁顺序的一致性,需要对程序中的加锁行为进行全局分析。单独分析每条获取多个锁的路径是不够的,因为单独看每个获取锁的方式看起来都是“合理”的。
但有时候并不能清楚地知道是否在锁顺序上有足够的控制权来避免死锁的发生。看如下转账的代码:
所有的线程似乎都是按照相同的顺序获得锁,但事实上锁的顺序取决于传递给加锁方法的参数顺序,而这些参数顺序又取决于外部输入,如果一个线程从X向Y转账,另一个线程从Y向X转账,就会发生死锁。
解决此问题必须定义锁的顺序,并在整个应用程序中都按照这个顺序来获取锁。制定锁的顺序时,要按照一个稳定的排序方式来排,比如通过System.identityHashCode方法,返回Object.hashCode值来作为排序的比较。
上面例子使用了加时锁,虽然增加了代价,但保证了安全性,而且使用加时锁的几率也非常低。如果排序时能获得唯一的键值进行排序那就更容易了,比如银行账号就可以用来作为唯一的键值,不需要加时锁。
1.1.2 在协作对象之间发生的死锁
某些获取多个锁的操作不像上面那么容易判断,获取两个锁的操作并不一定必须在一个方法中被获取。如A类的同步方法里使用了B类的同步方法,B类的同步方法也使用了A类的同步方法,那么一个线程使用A类,一个线程使用B类,也会出现交叉等待,
协同死锁。
1.1.3 开放调用
上面的情况,A类和B类并不知道他们会陷入死锁,而且他们本来也不应该知道。方法调用相当于一种抽象屏障,因而你无需了解在被调用方法中所执行的操作。也正是因为这种屏障,所以也难以分析可能出现的死锁。
如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用(Open Call).可以通过开放调用来避免死锁的发生,类似于采用封装机制来提供线程安全的的方法。
1.1.4 资源死锁
正如当多个线程相互持有彼此正在等待的锁而又不释放自己已持有的锁时会发生死锁。当他们在相同的资源集合上等待时,也会发生死锁。
1.2 死锁的避免与诊断
如果一个程序每次至多只获得一个锁,那么就不会产生lock-ording deadlock,但这并不现实。在使用细粒度的锁的过程中,可以通过使用一种两阶段策略来检查代码中的死锁:
(1)首先找出在什么地方将获取多个锁(使这个集合尽可能小),
(2)然后对所有这些实例进行全局分析,从而确保他们在整个程序中获取锁的顺序都保持一致。尽可能使用开放调用。
1.2.1 支持定时的锁
这一项技术可以检测死锁和从死锁中恢复过来,即显式使用Lock类中的定时
tryLock功能来代替内置锁机制。
1.2.2 通过线程转储信息来分析死锁
Jvm可以通过线程转储(Thread Dump)来帮助识别死锁的发生。
1.3 其他活跃性危险
尽管死锁是最常见的活跃性危险,但在并发程序中还存在一些其他的活跃性危险,包括饥饿,丢失信号,活锁等。
饥饿:线程由于无法访问它所需要的资源而不能继续执行。最常见的资源就是CPU时钟周期,如优先级低的线程由于其他高优先级线程一直执行导致无法获得cpu时间。
活锁:该问题不会阻塞线程,但也不能继续执行,因为线程不断重复执行相同的操作,而且总会失败。当多个相互协作的线程对彼此进行退让,修改各自的状态,但反复避让,导致活锁。就像两个相遇的人互相让路交叉等待。
- 大小: 14.2 KB
- 大小: 64.7 KB
- 大小: 74.8 KB
- 大小: 144.1 KB
- 大小: 26.1 KB
分享到:
相关推荐
包括检测超时、并发会话控制等功能,通过 `<session-management>` 配置可以实现这些功能。 #### 会话固定攻击防护 通过 `<session-fixation-protection>` 配置项来防止会话固定攻击。 #### OpenID 支持与属性交换...
在实际项目中,你可能还需要考虑事务管理、SQL语句优化、Druid的监控配置以及其他高级特性,以确保系统的稳定性和性能。通过上述步骤,你已经掌握了Spring MVC+Druid连接Informix/Sinoregal数据库的核心知识,可以...
### 经典高级并发编程实践:Active-Object 模式详解 #### 一、引言 并发编程一直是软件开发中的一个重要领域,特别是在分布式系统、网络应用程序以及多线程服务器的设计中。随着现代计算机硬件的发展,多核处理器...
ffuf -u <target_url> -w <wordlist_path> -e <extensions> -t <threads> -mc <match_codes> ``` 其中: - `-u` 是目标URL,指向要进行Fuzzing的服务器。 - `-w` 是单词列表文件,包含要尝试的字符串。 - `-e` 指定...
- **并发设计原则**:如最小化锁的使用,正确处理中断,避免活跃性问题等。 - **并发测试**:如何设计和执行并发测试,确保代码的正确性和性能。 这两本书结合阅读,可以帮助Java开发者从理论到实践全面掌握并发...
1. **负载测试**:JMeter可以模拟成千上万的并发用户,以此来测试服务器、网络或对象的性能。这种能力对于识别系统的瓶颈和优化性能至关重要。 2. **压力测试**:通过不断增加并发用户数量,JMeter可以帮助确定系统...
1.3.2 活跃性问题 1.3.3 性能问题 1.4 线程无处不在 第一部分 基础知识 第2章 线程安全性 2.1 什么是线程安全性 2.2 原子性 2.2.1 竞态条件 2.2.2 示例:延迟初始化中的竞态条件 2.2.3 复合操作 2.3 加锁...
1.3.2 活跃性问题 1.3.3 性能问题 1.4 线程无处不在 第一部分 基础知识 第2章 线程安全性 2.1 什么是线程安全性 2.2 原子性 2.2.1 竞态条件 2.2.2 示例:延迟初始化中的竞态条件 2.2.3 复合操作 2.3 加锁...
第三部分聚焦于性能优化,讲解了如何避免活跃性问题(如死锁和活锁)以及如何提高并发代码的性能和可伸缩性。此外,还介绍了测试并发代码正确性和性能的实用技巧,这对于在生产环境中确保程序的稳定性和效率至关重要...
1. **并发基础** - **线程与进程**:书中首先介绍了操作系统中的基本概念,如线程和进程,解释了它们的区别和交互方式。 - **Java线程API**:详细讨论了Java中的Thread类和Runnable接口,以及如何创建和管理线程。...
std::auto_ptr<cms::MessageConsumer> consumer = session->createConsumer(queue); ``` 发送和接收消息: ```cpp std::auto_ptr<cms::MessageProducer> producer = session->createProducer(queue); std::...
真正的Addison-Wesley 出品的Java Concurrency in Practice...第4部分 高级主题 第13章 显示锁 第14章 构建自定义的同步工具 第15章 原子变量与非阻塞同步机制 第16章 java存储模型 附录a 同步annotation 参考文献 索引
总之,《Java并发编程实战1》提供了全面而实用的并发编程指南,不仅涵盖了基础知识,还深入到高级主题,是Java开发者不可或缺的参考书。通过学习和实践书中的知识,开发者能够更好地应对并发编程的挑战,实现高效且...
1. **心跳机制:** 为了检测连接是否保持活跃,可以设置心跳包定期发送,如果一段时间内未收到对方的心跳响应,则认为连接已断开。 2. **数据编码与解码:** 数据在传输前可能需要进行编码(如JSON、XML或protobuf...
- 持久连接:保持WebSocket连接的活跃状态,避免因网络问题或服务器维护导致的断开。 - 消息队列:当处理大量并发连接时,可能需要队列来管理客户端的消息,以防止服务器过载。 6. 结合其他技术 WebSocket可以与...
9. **文档与社区支持**:作为一个成熟的Web容器,GoCat应该有详尽的文档和活跃的社区支持,帮助开发者快速上手和解决问题。 通过深入理解GoCat的这些特性,开发者可以更有效地构建高并发、低延迟的Web服务,同时...
4. **并发包JUC(Java Util Concurrency)**:JUC是Java提供的高级并发工具包,包含如Semaphore(信号量)、CountDownLatch(计数器)、CyclicBarrier(回环栅栏)、Exchanger(交换器)等工具类,它们简化了多线程...
《Java Concurrency in Practice》是Java并发编程领域的一本权威著作,由Brian Goetz、Tim Peierls、Joshua...阅读本书,不仅可以提升你的并发编程技能,还能帮助你在实际项目中避免并发问题,提高代码质量和可维护性。