java并发编程实践学习(2)--对象的组合

  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")或者别的来注释锁。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏后台全栈之路

图文并茂解释内存池原理

在 C 语言的动态申请内存技术中,相比起 alloc/free 系统调用,内存池(memory pool)优点很多。

7416
来自专栏kalifaの日々

GCJ 2008Round1AA 菜鸟踩坑(C++)

踩到的坑: 不同于POJ,GCJ有两个测试用例的文档,供你在本地得到输出,我开始的时候下载文档之后直接把文档中的数据复制出来,运行代码时贴上去,也就是,从标准输...

2625
来自专栏Java帮帮-微信公众号-技术文章全总结

Java并发学习4【面试+工作】

Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

1352
来自专栏瓜大三哥

Yaffs_guts

Yaffs_guts ? 1.Chunk的读写擦除 2.文件地址映射 3.文件系统对象 1.Chunk的读写擦除 我们知道,NAND Flash的基本擦除单位是...

19810
来自专栏xingoo, 一个梦想做发明家的程序员

MFC常用的类详细介绍

常用的MFC类 CRuntimeClass结构 在CRuntimeClass结构中定义了类名、对象所占存储空间的大小、类的版本号等成员变量及动态创建对象、派生关...

1925
来自专栏牛客网

知识总结:C++工程师106道面试题总结(含答案详解)

可以说个人秋招就要结束了,就等两个offer通知,然后签完搞定,这里提供一下自己复习的东西吧,我也就把这个东西给搞了一遍,然后面试基本没啥问题了,如果问的很深的...

3789
来自专栏Python与爬虫

再也不用担心网页编码的坑了!

大家爬取网页的时候,应该都遇到过这种情况 当我打印网页源代码的时候 发现 全部是乱码的 ? 那这个时候应该怎么办呢? requests是如何判断编码 首先,r...

2649
来自专栏技术博文

linux awk命令详解

简介 awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以...

35410
来自专栏C/C++基础

Linux命令(13)——cut命令

(1)以字符为单位输出指定范围的字符。 首先使用cat命令显示原文本文件内容如下:

642
来自专栏黑泽君的专栏

Linux的头文件和C/C++的头文件

=============================================================================

1572

扫码关注云+社区