java避免活锁.死锁的解决,java并发编程(九): 避免活跃性危险
避免活跃性危险:
本部分讨论活跃性故障的原因,及如何避免它们。
死锁:
典型的哲学家进餐问题。
锁顺序死锁:
如上面哲学家进餐有可能发生下面的情况:
上面发生死锁的根本原因在于两个线程以不同的顺序来获取相同的锁。
如果所有线程都以固定的顺序获取锁,那么程序就不会出现锁顺序死锁问题。
/**
* 容易因为获取锁的顺序导致死锁
*/
public class LeftRightDeadLock {
private final Object left = new Object();
private final Object right = new Object();
public void leftRight(){
synchronized(left){
synchronized(right){
// to do sth.
}
}
}
public void rightLeft(){
synchronized(right){
synchronized(left){
// to do sth.
}
}
}
}
动态的锁顺序死锁:
典型的就是银行转账问题
public void transferMoney(Account fromAccount, Account toAccount, int money){
synchronized (fromAccount) {
synchronized(toAccount){
if (fromAccount.getBalance() > money){
//余额不足
} else{
fromAccount.debit(money);
toAccount.credit(money);
}
}
}
} 当我们以下面这种方式调用,就有可能出现死锁:
transferMoney(a1, a2, money);
transferMoney(a2, a1, money); 要解决这种问题,就得使内部以相同的顺序加锁,无论外部怎么调用。
/**
* 用于当输入参数的hash值一样时使用
*/
private static final Object tieLock = new Object();
public static void transferMoney(final Account fromAccount,
final Account toAccount, final int money){
class Helper{
public void transfer(){
if (fromAccount.getBalance() < money){
//余额不足
} else{
fromAccount.debit(money);
toAccount.credit(money);
}
}
}
int fromHash = System.identityHashCode(fromAccount);
int toHash = System.identityHashCode(toAccount);
//无论客户端怎么传入参数,我们都以先锁定hash值小的,再锁定hash大的
//也可以利用业务中排序关系,如Account的编号等来比较
if (fromHash < toHash){
synchronized (fromAccount){
synchronized (toAccount) {
new Helper().transfer();
}
}
} else if (fromHash > toHash){
synchronized (toAccount){
synchronized (fromAccount) {
new Helper().transfer();
}
}
} else { //hash值相等, 情况很小
synchronized (tieLock) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().transfer();
}
}
}
}
} 在协作对象之间发生的死锁:
即在不同方法中相互持有等待得锁。
class Taxi {
private Point location;
private Point destination;
private final Dispatcher dispatcher;
public Taxi(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public synchronized Point getLocation(){
return location;
}
public synchronized void setLocation(Point location){
this.location = location;
if (location.equals(destination)){
dispatcher.notifyAvaliable(this);
}
}
}
class Dispatcher {
private final Set taxis;
private final Set avaliableTaxis;
public Dispatcher(){
taxis = new HashSet<>();
avaliableTaxis = new HashSet<>();
}
public synchronized void notifyAvaliable(Taxi taxi) {
avaliableTaxis.add(taxi);
}
public synchronized Image getImage(){
Image image = new Image();
for (Taxi t :taxis){
image.drawMarker(t.getLocation());
}
return image;
}
} 上面的
setLocation和
getImage就有可能发生死锁现象:setLocation获取到Taxi对象锁后,在dispacher.notifiyAvaliable()时需要dispatcher锁,而getImage获取到dispacher锁后,t.getLocation要求Taxi锁。
如果在持有锁时调用某个外部方法,那么将出现活跃性问题,在这个外部方法中可能会获取其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。
开放调用:
如果在调用某个方法时不需要持有锁,那么这种调用就被称为开放调用(Open Call)。
/**
* 通过公开调用来避免在相互协作的对象之间产生死锁
*/
public class OpenCall {
class Taxi {
private Point location;
private Point destination;
private final Dispatcher dispatcher;
public Taxi(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public synchronized Point getLocation(){
return location;
}
public void setLocation(Point location){
boolean reachedDestination;
synchronized(this){
this.location = location;
reachedDestination = location.equals(destination);
}
if (reachedDestination){
dispatcher.notifyAvaliable(this); //这里持有dispatcher锁,但已释放taxi锁
}
}
}
class Dispatcher {
private final Set taxis;
private final Set avaliableTaxis;
public Dispatcher(){
taxis = new HashSet<>();
avaliableTaxis = new HashSet<>();
}
public synchronized void notifyAvaliable(Taxi taxi) {
avaliableTaxis.add(taxi);
}
public Image getImage(){
Set copy;
synchronized (this) {
copy = new HashSet(taxis);
}
Image image = new Image();
for (Taxi t :copy){
image.drawMarker(t.getLocation());//调用外部方法前已释放锁
}
return image;
}
}
}
在程序中尽量使用开放调用。与那些在持有锁调用外部方法的程序时,更易于对依赖于开放调用的程序进行死锁分析。
资源死锁:
多个资源池(如数据库连接池),一个线程需要连接2个数据库连接,如线程A持有Pool1的连接,等待Pool2的连接;线程B持有Pool2的连接,等待Pool1的连接。
线程饥饿死锁,如一个任务中提交另一个任务,并一直等待被提交任务完成。
有界线程池/资源池与相互依赖的任务不能一起使用。
死锁的避免与诊断:
支持定时的锁:
限时等待。如Lock中的tryLock, 给定一个超时时限,若等待超过该时间,则会给出错误信息,避免永久等待。
通过线程转储信息来分析死锁:
可先通过jstack 获取线程栈信息。
其他活跃性危险:
饥饿:
由于线程无法访问它所需的资源而不能继续执行时,就发生了"饥饿"。
要避免使用线程优先级,这会增加平台依赖性,并可能导致活跃性问题,在大多数并发应用程序中,都可以使用默认的线程优先级。
糟糕的响应性:
例如GUI程序中使用了后台线程。该后台任务若为CPU密集型,将可能影响程序响应性。
不良的锁管理也可能导致糟糕的响应性。
活锁:
活锁是另一种形式的活跃性问题,该问题尽管不会阻塞线程,但也不能继续执行,因为线程将不断重复执行相同的操作,而且总会失败。
当多个相互协作的线程都对彼此进行响应从而修改各自的状态,并使得任何一个线程都无法继续执行时,就发生了活锁。
在并发应用中,通过等待随机长度的时间和回退可以有效的避免活锁的发生。
不吝指正。
java避免活锁.死锁的解决,java并发编程(九): 避免活跃性危险相关推荐
- java避免活锁.死锁的解决,死锁 活锁 饥饿 出现原因及解决方案
文章目录 死锁 概念 死锁示例 为什么会出现死锁呢? 如何解决死锁呢? 解决死锁代码实现 活锁 概念 活锁示例: 如何解决活锁呢? 饥饿 概念 如何解决饥饿呢? 死锁 概念 死锁:一组互相竞争资源的线 ...
- java redis队列_redis队列实现高并发怎么用?Java如何使用redis队列解决高并发?
小伙伴们大家好,不知道你们有没有在Java开发中遇到redis队列高并发,这个问题让你很头疼,今天小编就来讲解一下在Java中遇到redis队列高并发了,到底该怎么办. 高并发的业务场景: 我们做商品 ...
- Java高并发编程:活跃性危险
Java高并发程序中,不得不出现资源竞争以及一些其他严重的问题,比如死锁.线程饥饿.响应性问题和活锁问题.在安全性与活跃性之间通常存在依赖,我们使用加锁机制来确保线程安全,但是如果过度地使用加锁,则可 ...
- Java并发编程实战系列10之避免活跃性危险
10.1 死锁 哲学家问题 有环 A等B,B等A 数据库往往可以检测和解决死锁//TODO JVM不行,一旦死锁只有停止重启. 下面分别介绍了几种典型的死锁情况: 10.1.1 Lock orderi ...
- java并发编程(3)避免活跃性危险
活跃性危险 一.死锁 发生:每个人都不愿意放弃自己的锁,确想要别人的锁,这就会导致死锁 1.锁顺序死锁:如果每个线程以固定的顺序获取锁,那么至少在程序中不会出现锁顺序导致的死锁: 因为顺序固定如:所 ...
- java actor模型实例,详解Theron通过Actor模型解决C++并发编程的一种思维
现今,单台机器拥有多个独立的计算单元已经太常见了,这点在服务器的处理器上表现尤为明显,据AMD的一张2012-2013服务器路线图显示,服务器处理器的核心数将在2013年达到20颗之多,合理的利用CP ...
- 使用Java辅助类(CountDownLatch、CyclicBarrier、Semaphore)并发编程
在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法 一.C ...
- java cas是原子性的么_Java 并发编程:AQS 的原子性如何保证
当我们研究AQS框架时(对于AQS不太熟知可以先阅读<什么是JDK内置并发框架AQS>,会发现AbstractQueuedSynchronizer这个类很多地方都使用了CAS操作.在并发实 ...
- java jdk 未知错误_解决JAVA JDK安装出错的最常见问题,帮你排除困扰
一般来说,安装JAVA JDK的整个流程是很简单的,只要按照提示进行操作即可,就不会出现问题.但是呢,有小伙伴反映说,之前安装了JAVA JDK,进行卸载重装的时候出现错误提示,"正在进行另 ...
最新文章
- Mybatis-Plus一个新的报错:数据库表名与SQL的关键字冲突!!!
- 困扰程序员的30种软件开发问题,你是否时曾相识?
- 局部变量和常量的性能分析
- javascript --- [小练习]变量提升、优先级综合
- rtt面向对象oopc——5.IO设备管理之快速查看设备父类调用设备子类的方法
- matlab第七章符号对象,MATLAB语言:第七章 MATLAB符号计算
- 写在通用权限管理系统销售200套,从刚开始求人家用到人家主动索取,写一下亲身感受...
- slqmf刀模工具_CAD刀模绘图插件|CAD刀模绘图插件(SLQMF刀模工具)下载v3.0 免费版 - 欧普软件下载...
- 谷歌浏览器弹出Chrome版本太旧解决方式
- BP神经网络需要训练的参数,BP神经网络图像识别
- 把100PB数据迁移到阿里云,需要几步?
- 盘点2018年云计算热点:云原生、全栈云,云大脑,谁能独占鳌头?
- 【刷爆LeetCode】七月算法集训(14)栈
- python发邮件附件_python 发送带附件的邮件
- 抖音同款口红机 微信口红机 在线游戏口红机开发代码 分析
- BIEE 11g 安装
- 开发H5游戏练手, 黑暗堡垒-炼狱传奇H5 (一) 登陆界面开发
- C# SQLite 数据库基本操作
- MongoDB基础教程
- 原生态水平和垂直拉伸的JQUERY插件
热门文章
- C++创建对象new与不new区别
- tensorflow之exponential_decay
- python print rdd_spark: RDD与DataFrame之间的相互转换方法
- 什么是LAMP?LAMP有什么优势?
- python3 open函数_Python3 open函数
- python训练过程是什么_学了这么久,你知道Python机器学习全流程是怎样的么?
- 缓存击穿、缓存穿透、缓存雪崩简单总结
- Linux下静态库.a与.so库文件的生成与使用
- 计算机网络考研辅导谁的好,计算机网络考研辅导哪个好
- layui的表单控件的input文本框赋值