JAVA并发编程学习笔记------协作对象之间发生的死锁
一、 如果在持有锁时调用某个外部方法,那么将出现活跃性问题。在这个外部方法中可能会获取其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。如下代码:
public class Taxi {
private final Dispatcher dispatcher;
private Point location, destination;
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);
}
}
}
public class Dispatcher {
private final Set<Taxi> taxis;
private final Set<Taxi> avaliableTaxis;
public Dispatcher() {
taxis = new HashSet<Taxi>();
avaliableTaxis = new HashSet<Taxi>();
}
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和notifyAvailable都是同步方法,因此调用setLocation的线程将首先获得Taxi的锁,然后获取Dispatcher的锁,同样调用getImage的线程将首先获取Dispatcher的锁,然后再获取每一个Taxi的锁,两个线程按照不同的顺序来获取两个锁,这时就有可能产生死锁。
解决方案是开放调用(如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用),使同步代码块仅被用于保护那些涉及共享状态的操作,修改代码如下:
public class Taxi {
private final Dispatcher dispatcher;
private Point location, destination;
public Taxi(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public synchronized Point getLocation() {
return location;
}
public synchronized void setLocation(Point location) {
boolean reachedLocation;
synchronized (this) {
this.location = location;
reachedLocation = location.equals(destination);
}
if (reachedLocation) {
dispatcher.notifyAvaliable(this);
}
}
}
public class Dispatcher {
private final Set<Taxi> taxis;
private final Set<Taxi> avaliableTaxis;
public Dispatcher() {
taxis = new HashSet<Taxi>();
avaliableTaxis = new HashSet<Taxi>();
}
public synchronized void notifyAvaliable(Taxi taxi) {
avaliableTaxis.add(taxi);
}
public Image getImage(){
Set<Taxi> copy;
synchronized (this){
copy = new HashSet<Taxi>();
}
Image image = new Image();
for(Taxi t: copy){
image.drawMarker(t.getLocation());
}
return image;
}
}
在程序中应尽量使用开放调用,与那些在持有锁时调用外部方法的程序相比,更易于对依赖于开放调用的程序进行死锁分析。
二、除死锁外,还有几种活跃性危险,简单了解下:
(1)饥饿:当线程由于无法访问它所需要的资源而不能继续执行时,就发生了“饥饿”。
引发饥饿的最常见资源就是CPU始终周期。
(2)活锁:线程将不断重复执行某个消息,而且总会失败。
活锁通常发生在处理事务消息的应用程序中:如果不能成功的处理某个消息,那么消息处理机制将回滚整个事务,并将它重新放到队列的开头。
当多个相互协作的线程都对彼此进行响应从而修改各自的状态,并使得任何一个线程否无法继续执行时,就发生了活锁。
解决活锁的方案是在重试机制中引入随机性。
三、 减少锁的竞争:
1、有两个因素会影响在锁上发生竞争的可能性: 锁的请求频率,以及每次持有该锁的时间。
2、有三种方式可以降低锁的竞争程度:
1)减少锁的持有时间:实际情况中,仅当可以将一些“大量”的计算或阻塞操作从同步代码块中移出时,才应该考虑同步代码块的大小。
2)降低锁的请求频率;
3)使用带有协调机制的独占锁,这些机制允许更高的并发性。
JAVA并发编程学习笔记------协作对象之间发生的死锁的更多相关文章
- Java并发编程学习笔记
Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...
- Java并发编程学习笔记 深入理解volatile关键字的作用
引言:以前只是看过介绍volatile的文章,对其的理解也只是停留在理论的层面上,由于最近在项目当中用到了关于并发方面的技术,所以下定决心深入研究一下java并发方面的知识.网上关于volatile的 ...
- Java 并发编程学习笔记 理解CLH队列锁算法
CLH算法实现 CLH队列中的结点QNode中含有一个locked字段,该字段若为true表示该线程需要获取锁,且不释放锁,为false表示线程释放了锁.结点之间是通过隐形的链表相连,之所以叫隐形的链 ...
- Java并发编程学习笔记(一)——线程安全性
主要概念:线程安全性.原子性.原子变量.原子操作.竟态条件.复合操作.加锁机制.重入.活跃性与性能. 1.当多个线程访问某个状态变量并且其中有一个线程执行写入操作时,必须采用同步机制来协同这些线程对变 ...
- JAVA并发编程学习笔记------基础构建模块
一.并发容器:ConcurrentHashMap:1.分段锁机制: 任意数量的读取线程可以并发的访问map,执行读取操作的线程和执行写入操作的线程可以并发的访问Map,并且一定数量的写入线程可以并发的 ...
- JAVA并发编程学习笔记------对象的可见性及发布逸出
一.非原子的64位操作: 当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值,这种安全性保证被称为最低安全性.最低安全性适用于绝大多数变量 ...
- [转]JAVA并发编程学习笔记之Unsafe类
1.通过Unsafe类可以分配内存,可以释放内存:类中提供的3个本地方法allocateMemory.reallocateMemory.freeMemory分别用于分配内存,扩充内存和释放内存,与C语 ...
- JAVA并发编程学习笔记------多线程调优
1. 多线程场景下尽量使用并发容器代替同步容器 (如ConcurrentHashMap代替同步且基于散列的Map, 遍历操作为主要操作的情况下用CopyOnWriteArrayList代替同步的Lis ...
- Java并发编程学习笔记(三)——对象的组合
重要概念: 1.在设计线程安全类的过程中,需要包含以下三个基本要素: (1)找出构成对象状态的所有变量. (2)找出约束状态变量的不变性条件. (3)建立对象状态的并发访问管理策略. 2.
随机推荐
- SG 函数 S-Nim
http://poj.org/problem?id=2960 S-Nim Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 34 ...
- c语言变量类型联想
int float char *(指针) 已经定义:单个变量 单个相同类型在内存中顺序存放:数组 不同单个类型在内存中顺序存放:结构体 不同类型在内存中自由存放:链表 其中结构体与链表类型需运用时提前 ...
- Java集合系列[4]----LinkedHashMap源码分析
这篇文章我们开始分析LinkedHashMap的源码,LinkedHashMap继承了HashMap,也就是说LinkedHashMap是在HashMap的基础上扩展而来的,因此在看LinkedHas ...
- python之hashlib、configparser、logging模块
hashlib模块 Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等. 什么是摘要算法呢?摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长度固定的数 ...
- ABB安全区域(全局区域)的指令解析
VAR wztemporary wzone;//VAR:变量 //wztemporary:全局区域数据类型(wztemporary临时全局区域数据.wzstationary固定式全局区域)wzone: ...
- sublimeserver启动本地服务器(sublime text)
今天又get到了一个新知识点,就是在sublime text上也可以模拟一个本地服务器的环境,前提是要先安装sublimeserver这个插件.这个插件的安装办法有两种. 1.我们可以直接在subli ...
- java使用Websocket获取HttpSession出现的问题与解决
websocket的写法就不多说了,主要记一记其中出现的问题 1.获取不到httpSession 解决办法:先重写握手方法,将httpsession放入ServerEndpointConfig.get ...
- 百度分享到qq空间失败
QQ做了限制的,localhost是不会返回结果的,要用正式域名访问就可以了
- thinkphp3.2后台模块怎么添加(admin),直接复制Home?还是在入口文件生成?
1.都可以,复制home改下命名空间也行,在入口添加下参数自动生成也行 2ThinkPHP3.2后支持模块化开发,在Home目录的同级目录下创建一个新的文件夹,命名为Admin,或者就如你自己所说,直 ...
- OKMX6Q LTIB编译
因为在16.04上编译有许多解决不了的错误,最后还是在飞凌的12.04虚拟机上编译的. 按照手册<OKMX6X-S2-LTIB编译手册-V1.1-2016-08-18>进行到第8步时,出现 ...