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. 微信网页开发之创建Controller(三)

    首先,我们需要在App区域下添加一个控制器,这里我们就以AppDemoController为例.如下图所示: 注意:你也可以自己创建其他区域,只是这里推荐使用App区域. 创建好之后,请添加自己的Ac ...

  2. Redhat Linux /etc/profile 与 /etc/bashrc 的区别

    最近学习RHCE,在umask这里,书里说要修改/etc/profile和/etc/bashrc两个文件,却没有说明这两个区别.于是在上网查看之后倒是明白了各是怎么用的./etc/profile是对应 ...

  3. Javascript aop(面向切面编程)之around(环绕)

    Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...

  4. RabbitMQ(五) -- topics

    RabbitMQ(五) -- topics `rabbitmq`中的`topic exchange`将路由键和某模式进行匹配,从而类似于正则匹配的方式去接收喜欢的信息. topic exchange ...

  5. JavaScript 数组详解

    在程序语言中数组的重要性不言而喻,JavaScript中数组也是最常使用的对象之一,数组是值的有序集合,由于弱类型的原因,JavaScript中数组十分灵活.强大,不像是Java等强类型高级语言数组只 ...

  6. 翻译:AKKA笔记 - Actor消息 -1(二)

    消息 我们只是让QuoteRequest到ActorRef去但是我们根本没见过消息类! 它是这样的: (一个最佳实践是把你的消息类包装在一个完整的对象里以利于更好的组织) TeacherProtoco ...

  7. IOS UIView 03- 自定义 Collection View 布局

    注:本人是翻译过来,并且加上本人的一点见解. 前言 UICollectionView 在 iOS6 中第一次被引入,也是 UIKit 视图类中的一颗新星.它和 UITableView 共享一套 API ...

  8. linux service

    有些东西真是难得搞懂,一旦懂了就容易记住了. 说到service 就不能不说 daemon, 他们两者看起来不相关.其实是紧密相连的两个概念. —— 就像两个同心的正五边形和正六边形放在一起时候的样子 ...

  9. 每天一个linux命令(44):top命令

    top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.下面详细介绍它的使用方法.top是一个动态显示过程,即可以通过用户按键来不断刷新 ...

  10. ServletConfig对象详解

    在Servlet 的配置文件中,可以用一个或多个<init-param>标签为servlet配置一些初始化参数. 当servlet配置了初始化参数之后,web容器在创建servlet实例对 ...