Java并发编程阅读笔记-Java监视器模式示例
1、前言
书中在解释Java监视器模式的时候使用了一个车辆追踪器例子,根据不同的使用场景给出了不同的实现和优化。
2、监视器模式示例
实现一个调度车辆的“车辆追踪器”,每台车使用一个String对象标识,并且拥有一个相应的位置坐标(x,y)。由于运行在多线程的场景下,对外暴露的接口需要保证线程安全。
需要提供的接口包括:
- 获取所有车辆标识和位置
- 读取某个车辆位置
- 更新某个车辆位置
下面给出第一种实现:
@ThreadSafe
public class MonitorVehicleTracker {
@GuardedBy("this")
private final Map<String, MutablePoint> locations; public MonitorVehicleTracker(Map<String, MutablePoint> points) {
locations = deepCopy(points);
} public synchronized Map<String, MutablePoint> getLocations() {
return deepCopy(locations);
} public synchronized MutablePoint getLocation(String key) {
MutablePoint point = locations.get(key);
return point == null ? null : new MutablePoint(point);
} public synchronized void setLocation(String id, int x, int y) {
if (id == null) {
return;
}
MutablePoint point = locations.get(id);
if (point == null) {
throw new IllegalArgumentException("No such ID: " + id);
}
point.setPoint(x, y); } private Map<String, MutablePoint> deepCopy(Map<String, MutablePoint> points) {
if (points == null) {
return Maps.newHashMap();
}
Map<String, MutablePoint> result = Maps.newHashMapWithExpectedSize(points.size());
for (String key : points.keySet()) {
result.put(key, new MutablePoint(points.get(key)));
}
return Collections.unmodifiableMap(result);
}
} @NotThreadSafe
public class MutablePoint {
private int x, y; public MutablePoint(int x, int y) {
this.x = x;
this.y = y;
} public MutablePoint() {
} public MutablePoint(MutablePoint point) {
if (point == null) {
throw new IllegalArgumentException("param is null");
}
int[] pointArray = point.getPointArray();
x = pointArray[0];
y = pointArray[1];
} public int[] getPointArray() {
int[] ret = new int[2];
ret[0] = x;
ret[1] = y;
return ret;
} public void setPoint(int x, int y) {
this.x = x;
this.y = y;
}
}
首先需要定义一个表示位置的类MutablePoint,该类是可变的,非线程安全的。车辆追踪器的逻辑实现在类MonitorVehicleTracker,提供了所需的三种接口逻辑。MonitorVehicleTracker是一个线程安全类,通过java内置锁(synchronized)和深度拷贝实现,返回的位置信息拷贝了当前的数据,包括车辆表示和对应的位置信息。这种实现方式得到的位置信息是当前的快照,这样的数据结果是否合适取决于你的需求。
上面这个实现使用了深度拷贝的方式,这种方式在车辆数量非常大的时候存在性能问题。那么是否可以直接返回原有的数据呢,答案是不可以,如果直接返回,这样意味着直接发布了不支持线程安全的HashMap结构,该数据会在多个线程将共享。
那么我们是否有其他方式解决这个问题呢。一种方案是将线程安全的能力委托给类中内部组件,而java提供了线程安全的HashMap-concurrentHashMap(HashTable、Collections.synchronizedMap()性能不及ConcurrentHashMap)
下面给出第二种实现:
@ThreadSafe
public class MonitorVehicleTracker {
private final ConcurrentHashMap<String, ImmutablePoint> locations;
private final Map<String, ImmutablePoint> unmodifiedLocations; public MonitorVehicleTracker(Map<String, ImmutablePoint> pointMap) {
locations = new ConcurrentHashMap<>(pointMap);
unmodifiedLocations = Collections.unmodifiableMap(locations);
} public Map<String, ImmutablePoint> getLocations() {
return unmodifiedLocations;
} public void setLocation(String id, int x, int y) {
if (StringUtils.isBlank(id)) {
return;
}
if (locations.replace(id, new ImmutablePoint(x, y)) == null) {
throw new IllegalArgumentException("No such ID: " + id);
}
} public ImmutablePoint getLocation(String id) {
if (StringUtils.isBlank(id)) {
throw new IllegalArgumentException("param is null");
}
return locations.get(id);
}
} @Immutable
@ThreadSafe
public class ImmutablePoint {
private final int x, y; public ImmutablePoint(int x, int y) {
this.x = x;
this.y = y;
} public ImmutablePoint(MutablePoint point) {
if (point == null) {
throw new IllegalArgumentException("param is null");
}
int[] pointArray = point.getPointArray();
x = pointArray[0];
y = pointArray[1];
} public int[] getPointArray() {
int[] ret = new int[2];
ret[0] = x;
ret[1] = y;
return ret;
}
}
第二个实现中,MonitorVehicleTracker类的线程安全能力委托给内部组件。因为ConcurrentHashMap本身是一个线程安全的HashMap,所以无需进行深度拷贝,直接在线程间共享该数据结构即可。从上面的实现可以看到,位置信息使用ImmutablePoint而不是MutablePoint,这是因为位置信息也会发布出去,也可能会在线程间共享,而ConcurrentHashMap只能保证自身操作的线程安全。ConcurrentHashMap的key、value都需要是线程安全的,ImmutablePoint使用不变性提供了线程安全,String可以认为是常量,同样支持线程安全。与第一种实现发放不同的是,每个线程拿到的位置信息视图是一个变化的,并非快照,如果需要快照,通过浅拷贝即可实现。
实现一个线程安全的位置信息类还可以通过内置锁实现,同样,整个MonitorVehicleTracker类还是线程安全的。
上面这个实现通过委托给支持线程安全的内部组件实现线程安全,那么是不是只要内部组件是线程安全的那这个类就是线程安全的呢,显然不是的,如果内部组件的数据存在逻辑关系,或者存在复合操作时,线程安全需要满足既定的逻辑关系,保证符合操作的原子性,这些都是需要额外的同步操作来完成
在扩展原有支持线程安全类的时候,不管是通过继承方式还是组合方式(客户端加锁),都需要保证扩展类中加的锁和基类的锁是一个锁。
Java并发编程阅读笔记-Java监视器模式示例的更多相关文章
- Java并发编程阅读笔记-锁和活跃性问题
- Java并发编程阅读笔记-同步容器、工具类整理
- Java并发编程系列-(5) Java并发容器
5 并发容器 5.1 Hashtable.HashMap.TreeMap.HashSet.LinkedHashMap 在介绍并发容器之前,先分析下普通的容器,以及相应的实现,方便后续的对比. Hash ...
- Java并发编程系列-(6) Java线程池
6. 线程池 6.1 基本概念 在web开发中,服务器需要接受并处理请求,所以会为一个请求来分配一个线程来进行处理.如果每次请求都新创建一个线程的话实现起来非常简便,但是存在一个问题:如果并发的请求数 ...
- Java并发编程系列-(7) Java线程安全
7. 线程安全 7.1 线程安全的定义 如果多线程下使用这个类,不过多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的. 类的线程安全表现为: 操作的原子性 内存的可见性 不 ...
- Java并发编程学习笔记
Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...
- Java并发编程实战.笔记十一(非阻塞同步机制)
关于非阻塞算法CAS. 比较并交换CAS:CAS包含了3个操作数---需要读写的内存位置V,进行比较的值A和拟写入的新值B.当且仅当V的值等于A时,CAS才会通过原子的方式用新值B来更新V的值,否则不 ...
- Java并发编程读书笔记(一)
----------------------------------------------<Java并发编程实战>读书笔记-------------------------------- ...
- Java并发编程:JMM(Java内存模型)和volatile
1. 并发编程的3个概念 并发编程时,要想并发程序正确地执行,必须要保证原子性.可见性和有序性.只要有一个没有被保证,就有可能会导致程序运行不正确. 1.1. 原子性 原子性:即一个或多个操作要么全部 ...
随机推荐
- Android的颜色值转换
Android的颜色int值比较变态,是个负值,用计算机术语讲叫补码,手工转换比较麻烦,首先看看文档 https://developer.android.com/reference/android/g ...
- 兼容 Android 4.4 透明状态栏与导航栏
http://www.apkbus.com/Android-163388-1-1.html?_dsign=73d41229 android 系统自4.2 开始 UI 上就没多大改变,4.4 也只是增加 ...
- 爬取廖雪峰的python3教程
从廖雪峰老师的python教程入门的,最近在看python爬虫,入手了一下 代码比较low,没有用到多线程和ip代理池 然后呢,由于robots.txt的限定,构建了一些user-agent,并放慢的 ...
- Java多线程:wait(),notify(),notifyAll()
1. wait(),notify(),notifyAll() 2. wait() 2.1. wait() 2.2. wait(long timeout) 2.3. wait(long timeout, ...
- SpringMVC解决跨域的两种方案
1. 什么是跨域 2. 跨域的应用情景 3. 通过注解的方式允许跨域 4. 通过配置文件的方式允许跨域 1. 什么是跨域 跨域,即跨站HTTP请求(Cross-site HTTP request),指 ...
- 万网主机使用wordpress发送邮件的方法
今天弄了一下午总算明白了,这里写一下具体过程. 首先是邮箱,万网主机是不支持mail()函数的,所以默认的不可用,如果你想发送邮件的话,只能使用fsockopen()函数.首先进入万网主机管理平台,启 ...
- lambda隐藏函数的嵌套
# 隐藏函数嵌套 f = (lambda a,b :a if a>b else b)(1000, 2000008) print((lambda a,g:a if a > g else g) ...
- 2014金山笔试_编写一个数组类 MyVector
//编写一个数组类 MyVector,数组内容可以动态扩充,实现构造,析构,赋值操作符重载,插入,删除,获取元素个数,获取数组容量(不可以使用STL等的容器类,不能使用 //不连续的存储空间) #in ...
- Maven学习(三)-- 使用Maven构建项目
摘自:http://www.cnblogs.com/xdp-gacl/p/4240930.html maven作为一个高度自动化构建工具,本身提供了构建项目的功能,下面就来体验一下使用maven构建项 ...
- 并发库应用之五 & ReadWriteLock场景应用
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...