希望将一些现有的线程安全组件组合为更大规模的组件或程序

设计线程安全的类

如果对象中所有的域是基本类型变量,那么这些域将构成对象的全部状态。例如,LinkedList的状态就包括该链表中所有节点对象的状态。要确保线程的安全性,就需要确保它的不变性条件不会在并发访问的情况下被破坏。

实例封闭

当一个对象被封装到另一个对象中时,能够访问被封装对象的所有代码路径都是已知的。通过将封闭机制与合适的加锁策略结合起来,可以确保以线程安全的方式来使用非线程安全的对象。被封闭对象一定不能超出它们既定的作用域。来个例子

如果Person类是可变的,那么在访问从PersonSet中获得的Person对象时,还需要额外的同步。在java平台的类库中有很多线程封闭的实例,其中有些类的唯一用途是将非线程安全的类转化为线程安全的类。一些基本容器类并非线程安全的,例如ArrayList和Hash,但类库提供了包装器工厂方法(例如Collections.synchronizedList及其类似方法),使得这些线程非安全的类可以在多线程环境中安全地使用。这些工厂方法通过“装饰器”模式将容器类封装在一个同步的包装器对象中,而包装器能将接口中的方法都实现为同步方法,并将调用请求转发到底层的容器对象上。只要包装器对象拥有对底层容器对象的唯一引用,那么他就是线程安全的。

java监视器模式

把所有可变状态都封装起来,并由对象自己的内置锁来保护。如:

看到了有意思的部分了:

使用私有锁而不是对象的内置锁(或任何其他可通过公有方式访问的锁), 有许多优点。私有的锁对象可以将锁封装起来,是客户代码无法得到锁,但客户代码可以通过公有方法来获得锁。这是书上的原话,然而看不懂....。首先要搞明白的是私有锁和内置锁的区别,在网上看到一篇不错的博文:http://www.jb51.net/article/56440.htm

1. 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段。

2.对象锁:在代码中的方法上加了synchronized的锁,或者synchronized(this)的代码段。

3.私有锁:在类内部声明一个私有属性如private Object lock,在需要加锁的代码段synchronized(lock)。

4.内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

看来内置锁和对象锁是一回事了,只不过角度不同定义自然不一样。

示例:车辆追踪

一个用于调度车辆的“车辆追踪器”,例如出租车,警车,货车等。首先使用监视器模式来构建车辆追踪器,然后在尝试放宽某些封装性需求同时又保持线程安全性。

执行更新操作的线程通过从GPS设备上获取的数据或者调度员从GUI界面上输入的数据来修改车辆的位置

视图线程与执行更新操作的线程将并发地访问数据模型,因此该模型必须是线程安全的,下面的程序给出了一个基于java监视器模式实现的“车辆追踪器”,其中使用了程序清单4-5中的MutablePoint来表示车辆的位置。

虽然类MutablePoint不是线程安全的,但追踪器类是线程安全的。它所包含的Map对象和可变的Ponit对象都未曾发布,当需要返回车辆的位置时,通过MutablePoint拷贝构造函数或者deepCopy方法来复制正确值,从而生成一个新的Map对象,并且该对象中的值与原有Map对象中的key和value值都相同。

线程安全性的委托

示例:基于委托的车辆追踪器

构造一个委托给线程安全类的车辆追踪器。将车辆的位置保存到一个Map对象中,因此首先要实现一个线程安全的Map类ConcurrentHashMap。还可以用一个不可变的类Point来代替MutablePoint以保存位置, 

如果使用最初的MutablePoint类而不是Point类就会破坏封装性,因为getLocations会发布一个指向可变状态的引用。这里我对监视器模式和委托之间的区别的理解是:监视器将所有可变状态封装,对这些状态的操作利用内置锁保护起来。而委托模式则是将这些状态的安全性委托给一些线程安全的容器来保护。上面的代码还有一点要注意的是:getLocations返回的是一个不可修改但却实时的车辆位置视图。

独立的状态变量

将线程的安全性委托给多个状态变量。只要这些变量是彼此独立的。

当委托失效时

状态变量之间存在着某些不变性条件,如:

此时仅靠委托机制并不足以实现线程安全性,这个类必须提供自己的加锁机制。

发布底层的状态变量

示例:发布状态的车辆追踪器

在这个版本中发布底层的可变状态。

在现有的线程安全类中添加功能

简而言之就是利用java所提供的安全类库,再添加我们自己的类,来确保我们要实现的程序功能的线程安全性。

客户端加锁机制

参考:https://www.cnblogs.com/yaowen/p/5983136.html

组合

当为现有的类添加一个原子操作时,有一种更好的方法:组合。

使用了java监视器模式来封装了现有的List,只要在类中拥有指向底层List的唯一外部引用,就能确保线程安全性。(注意这里的底层List实例是私有不可变的)


心得:这章的主要内容是当多个对象组合在一起时如何确保其线程安全性。感觉这章真的是很抽象,说了很多理论性的东西,现在还是有很多不清楚,不明白的地方,还是得靠多实践才能加深理解。

《java并发编程实战》读书笔记3--对象的组合的更多相关文章

  1. Java并发编程实战 读书笔记(一)

    最近在看多线程经典书籍Java并发变成实战,很多概念有疑惑,虽然工作中很少用到多线程,但觉得还是自己太弱了.加油.记一些随笔.下面简单介绍一下线程. 一  线程与进程   进程与线程的解释   个人觉 ...

  2. Java并发编程实战 读书笔记(二)

    关于发布和逸出 并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的this引用就被发布出去了.这是危及到线程安全的,因为其他线程有可能通过这个 ...

  3. 【JAVA并发编程实战】2、对象的组合

    1. 设计线程安全的类 1.找出构成对象状态的所有变量 2.找出约束状态变量的不变性条件 3.建立对象状态的并发访问管理策略 package cn.xf.cp.ch04; /** * *功能:JAVA ...

  4. Java并发编程实战 第4章 对象的组合

    Java监视器模式 java监视器模式就是在将共享的数据封装在一个类里面,然后然后所有访问或者修改这些数据的方法都标注为synchronize. 车辆追踪模拟: 使用监视器模式: CarTracker ...

  5. 《java并发编程实战》笔记

    <java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为:  Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...

  6. Java多线程编程实战读书笔记(一)

    多线程的基础概念本人在学习多线程的时候发现一本书——java多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.

  7. 读书笔记-----Java并发编程实战(二)对象的共享

    public class NoVisibility{ private static boolean ready; private static int number; private static c ...

  8. Java并发编程实践读书笔记(1)线程安全性和对象的共享

    2.线程的安全性 2.1什么是线程安全 在多个线程访问的时候,程序还能"正确",那就是线程安全的. 无状态(可以理解为没有字段的类)的对象一定是线程安全的. 2.2 原子性 典型的 ...

  9. Java并发编程艺术读书笔记

    1.多线程在CPU切换过程中,由于需要保存线程之前状态和加载新线程状态,成为上下文切换,上下文切换会造成消耗系统内存.所以,可合理控制线程数量. 如何控制: (1)使用ps -ef|grep appn ...

  10. Java并发编程实践读书笔记(2)多线程基础组件

    同步容器 同步容器是指那些对所有的操作都进行加锁(synchronize)的容器.比如Vector.HashTable和Collections.synchronizedXXX返回系列对象: 可以看到, ...

随机推荐

  1. 51nod 1275 连续子段的差异(twopointer+单调队列)

    对于每一个i找到最近的j满足最大值-最小值>K,对答案的贡献为j-i,用单调队列维护最值即可 #include<iostream> #include<cstdlib> # ...

  2. Codeforces Round #393 (Div. 2) (8VC Venture Cup 2017 - Final Round Div. 2 Edition)A 水 B 二分 C并查集

    A. Petr and a calendar time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  3. Codeforces Round #169 (Div. 2) A水 B C区间更新 D 思路

    A. Lunch Rush time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  4. POJ 2289 最大流

    Jamie's Contact Groups Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 7624   Accepted: ...

  5. hibernate 如何配置两个属性唯一

    在单一字段的唯一性约束时,我们可以在映射文件里配置property属性的unique="true"来达到目的,但多字段的唯一性约束怎样处理呢?如 果使用复合主键可以很简单地解决这个 ...

  6. 利用pdfJS实现以读取文件流方式在线展示pdf文件

    第一步:下载源码https://github.com/mozilla/pdf.js 第二步:构建PDF.js 第三步:修改viewer.js var DEFAULT_URL = 'compressed ...

  7. 【JSP EL】EL表达式获取当前时间(两种方式)

    第一种方式: //先在代码段定义<% long date = new Date().getTime(); request.setAttribute("date", date) ...

  8. [洛谷P1822] 魔法指纹

    洛谷题目连接:魔法指纹 题目描述 对于任意一个至少两位的正整数n,按如下方式定义magic(n):将n按十进制顺序写下来,依次对相邻两个数写下差的绝对值.这样,得到了一个新数,去掉前导0,则定义为ma ...

  9. 知问前端——Ajax表单插件

    传统的表单提交,需要多次跳转页面,极大的消耗资源也缺乏良好的用户体验.而这款form.js表单的Ajax提交插件将解决这个问题. 一.核心方法 官方网站:http://malsup.com/jquer ...

  10. Little Mathematics Knowledge 数学小常识

    The sum of arithmetic sequence The sum of geometric sequence A special formula : n·n! = (n+1)! - n! ...