1. 先验条件(Precondition):某些方法包含基于状态的先验条件。例如,不能从空队列中移除一个元素,在删除元素前队列必须处于非空状态。基于状态的先验条件的操作成为依赖状态操作。
  2. 在单线程中,如果某操作无法满足先验条件,就只能失败,但在并发程序中先验条件可能会由于其他线程执行的操作而变成真。
  3. java中等待某个条件为真的各种内置机制(包括等待和通知机制)都与内置加锁紧密关联。
  4. 所有权和封装性总是相关联的:对象封装它拥有的所有权,对象对它的封装的状态拥有所有权。
  5. 发布了某个可变对象的引用,那就不再拥有独占的控制权。
  6. 容器类通常表现出一种“所有权分离”的形式。

4.1设计线程安全的类

在设计线程安全类的过程中,需要包含以下三个基本要素:

  • 找出构成对象状态的所有变量
  • 找出约束状态变量的不可变性条件
  • 建立对象状态的并发访问管理策略

4.3委托给线程安全的类

可以将共享资源委托给一个线程安全的类。比如ConcurrentHashMap,copyOnWriteArrayList.

如果一个类时由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效状态转换,那么可以将线程安全性委托给底层状态变量。

下面是一个监控车辆位置的实例。其中Point是线程安全不可变的类。

/**
* 不可变
*/
@Immutable
class Point{
public final int x,y; Point(int x,int y ) {
this.x = x;
this.y = y;
}
} /**
* 委托给线程安全类的车辆追踪器
*/
@ThreadSafe
class DelegatingVehicleTracker{
private final ConcurrentHashMap<String,Point> locations;
private final Map<String,Point> unmodifiableMap; public DelegatingVehicleTracker(Map<String,Point> points) {
this.locations = new ConcurrentHashMap<>(points);
this.unmodifiableMap = Collections.unmodifiableMap(locations);
} public Map<String,Point> getLocations(){
return unmodifiableMap;
} public Point getLocation(String id){
return locations.get(id);
} public void setLocation(String id,int x,int y){
if(locations.replace(id,new Point(x,y)) == null){
throw new IllegalArgumentException("invalid vehicle name:"+id);
}
}
}

  如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量。

同样是车辆追踪,我想要获取位置,还可以修改位置,安全性问题可以交给底层SafePoint:

/**
* 线程安全且可变的Point类
*/
@ThreadSafe
class SafePoint{
@GuardedBy("this") private int x,y;
private SafePoint(int[] a){
this(a[0],a[1]);
}
public SafePoint(SafePoint p){
this(p.get());
}
public SafePoint(int x,int y){
this.x = x;
this.y = y;
}
public synchronized int[] get(){
return new int[] {x,y};
}
public synchronized void set(int x,int y){
this.x =x;
this.y = y;
}
} /**
* 安全发布底层状态的车辆追踪器
*/
@ThreadSafe
class PublishingVehicleTracker{
private final Map<String,SafePoint> locations;
private final Map<String,SafePoint> unmodifiableMap; PublishingVehicleTracker(Map<String, SafePoint> locations, Map<String, SafePoint> unmodifiableMap) {
this.locations = locations;
this.unmodifiableMap = unmodifiableMap;
} public Map<String,SafePoint> getLocations(){
return unmodifiableMap;
}
public SafePoint getLocation(String id){
return locations.get(id);
}
public void setLocation(String id,int x,int y){
if (!locations.containsKey(id))
throw new IllegalArgumentException("invalid vehicle name:"+id);
locations.get(id).set(x,y);
}
}

  4.5将同步策略文档化

在文档中说明客户代码需要了解的线程安全性保证,以及代码维护人员需要了解的同步策略。

synchronized,volatile或者任何一个线程安全类都对应于某种同步策略,用于在并发访问时确保数据的完整性。一定要在忘记之前记录下来。

可以使用@GuardedBy("this")或者别的来注释锁。

java并发编程实践学习(2)--对象的组合的更多相关文章

  1. Java并发编程实战 之 对象的共享

    上一篇介绍了如何通过同步多个线程避免同一时刻访问相同数据,本篇介绍如何共享和发布对象,使它们被安全地由多个进程访问. 1.可见性 通常,我们无法保证执行读操作的线程能看到其他线程写入的值,因为每个线程 ...

  2. JAVA并发编程学习笔记------对象的可见性及发布逸出

    一.非原子的64位操作: 当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值,这种安全性保证被称为最低安全性.最低安全性适用于绝大多数变量 ...

  3. 《Java并发编程实战》学习笔记 线程安全、共享对象和组合对象

    Java Concurrency in Practice,一本完美的Java并发参考手册. 查看豆瓣读书 推荐:InfoQ迷你书<Java并发编程的艺术> 第一章 介绍 线程的优势:充分利 ...

  4. [Java 并发] Java并发编程实践 思维导图 - 第四章 对象的组合

    依据<Java并发编程实践>一书整理的思维导图. 第一部分: 第二部分:

  5. Java 并发编程(四):如何保证对象的线程安全性

    01.前言 先让我吐一句肺腑之言吧,不说出来会憋出内伤的.<Java 并发编程实战>这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住.因为第四章"对象的组合 ...

  6. Java并发编程锁系列之ReentrantLock对象总结

    Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...

  7. 《Java并发编程实战》第三章 对象的共享 读书笔记

    一.可见性 什么是可见性? Java线程安全须要防止某个线程正在使用对象状态而还有一个线程在同一时候改动该状态,并且须要确保当一个线程改动了对象的状态后,其它线程能够看到发生的状态变化. 后者就是可见 ...

  8. 【Java并发编程一】线程安全和共享对象

    一.什么是线程安全 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用代码代码不必作其他的协调,这个类的行为仍然是正确的,那么称这个类是线程安全的 ...

  9. Java并发编程(五):Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...

随机推荐

  1. JAVA CAS原理深度分析-转载

    参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...

  2. phoenix 开发API系列(三)phoenix api 结合数据库

    概述 介绍了 api 的各种写法之后,下面介绍构建 api 时与数据库连接的方式. 注 下面使用的工程的完整代码已经公开在: http://git.oschina.net/wangyubin/phoe ...

  3. ListView用法总结

    前言 列表,它作为一种非常重要的显示形式,不管是在web端还是在移动平台上,都是一种非常友好的,功能强大的展现形式.在Android中,ListView就接管了这一重任.尽管在Android5.X时代 ...

  4. ffmpeg进行视频转换

    参考 Mencoder使用方法 Windows下MEncoder下载和Linux下MEncoder的编译 如何降低FFMpeg.exe使用过程中的Cpu使用率 实际上是通过参数控制服务器上用于ffmp ...

  5. 找回忘记的Ubuntu用户名和密码

    找回忘记的Ubuntu用户名和密码 前端时间使用VMWare安装了个Ubuntn的虚拟机,但是烦于安装后显卡驱动的问题,看着操作界面就有点厌烦,就扔下了.今天打开虚拟机登陆的时候忘了密码,寻思着难道要 ...

  6. CoinPunk项目介绍

           CoinPunk是一个bitcoin比特币钱夹服务web应用程序,你可以自己构建钱夹服务.开源,免费. 轻量级,高效 响应式设计 轻易创建新账户 详细的交易记录 构建于Node.js与H ...

  7. node(md5)

    md5是一种信息-摘要算法,即针对一段明文给出一个hash值,在密码学中最经典的用法是验证数据的完整性,因为一旦原始数据发生改变那么生成的摘要也必将不同. 网络中md5可以用于用户密码的加密,即在数据 ...

  8. zendframework 2

    我想我的生活需要新的挑战 zf2整个框架里面都应用了namespace,并且他的每个模块,我们都可以根据自己的需要去命名路径,对我来说,zf2的模块化更加的清晰,对于外包来说,或许很方便. 创建他,我 ...

  9. Unity 热更新实例一、C#Light 和UI系统使用实例

    接下来我会运用热更新的机制,逐步制作一些例子来阐释脚本系统如何和Unity结合. 脚本不限于使用C#Lite,但是C#Lite会有一些便利之处,请往下看. 结合机制也不限于这一种,但是C#Lite的设 ...

  10. JS中的闭包

    在复习JS高程的过程中,在第七章闭包那节,发现自己看不太明白了.之前看高程的时候,都弄得挺清楚了,怎么现在反而又看不懂了. 嗯,也许更深层次的东西涉及到编译原理的知识了.嗯,在研究完SPA后就开始学习 ...