java并发编程实战-第4章-对象的组合
(重点:组合模式)
4.3.5 重点:理解发布状态可变的车辆追踪器
p58 理解脚注: 如果将拷贝构造函数实现为this(p.x,p.y),那么会产生竞态 条件,而私有构造函数则可以避免这种竞态条件.这是私有构造函数捕获模式的一个实例
测试类
public class SafePointMain {
public static void main(String[] args) throws Exception {
final SafePoint originalSafePoint = new SafePoint(1,1);
//One Thread is trying to change this SafePoint
new Thread(new Runnable() {
@Override
public void run() {
originalSafePoint.setXY(2, 2);
System.out.println("Original : " + originalSafePoint.toString());
}
}).start();
//The other Thread is trying to create a copy. The copy, depending on the JVM, MUST be either (1,1) or (2,2)
//depending on which Thread starts first, but it can not be (1,2) or (2,1) for example.
new Thread(new Runnable() {
@Override
public void run() {
SafePoint copySafePoint = new SafePoint(originalSafePoint);
System.out.println("Copy : " + copySafePoint.toString());
}
}).start();
}
}
安全类:使用私有构造函数
@ThreadSafe
public 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;
}
@Override
public String toString() {
return "("+x+","+y+")";
}
}
用SafePointMain测试的结果(真确)
如果不用私有构造函数,而直接使用下面类SafePoint2,
class SafePoint2 {
private int x;
private int y;
public SafePoint(int x, int y){
this.x = x;
this.y = y;
}
public SafePoint(SafePoint safePoint){
this(safePoint.x, safePoint.y);
}
public synchronized int[] getXY(){
return new int[]{x,y};
}
public synchronized void setXY(int x, int y){
this.x = x;
//Simulate some resource intensive work that starts EXACTLY at this point, causing a small delay
try {
Thread.sleep(10 * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.y = y;
}
public String toString(){
return Objects.toStringHelper(this.getClass()).add("X", x).add("Y", y).toString();
}
}
则测试结果,:
Copy : (2,1)
Original : (2,2)
本质上,是写(setXY) 与读(构造函数中的 this(safePoint.x, safePoint.y);)不同步造成的,而构造函数不不能使用同步机制的
但是getXY方法是安全的,所以构造一个接受getXY参数的构造函数
private SafePoint(int [] xy){
this(xy[0], xy[1]);
}
And then, the actual invokation:
public SafePoint (SafePoint safePoint){
this(safePoint.getXY());
}
Notice that the constructor is private, this is because we do not want to expose yet another public constructor and think again about the invariants of the class, thus we make it private - and only we can invoke it.
具体参考stackoverflow上Eugene的精彩的解答:http://stackoverflow.com/questions/12028925/private-constructor-to-avoid-race-condition/12029346
4.1 设计线程安全的类
三个基本要素
变量、约束状态变量的不变性条件、建立对象状态并发访问的管理策略
同步策略定义了如何在不违背对象的不边条件或者后验条件的情况下对其状态的访问进行协同。
同步策略规定了如何将不变性、线程封闭、加锁机制等结合起来维护线程的安全性。规定了哪些变量由哪写所保护,为了开发人员可以对这个类进行分析与维护,还得把同步策略写成文档
4.1.1 收集同步需求
比如,
Count类中的变量的long的取值范围、不能为负数
当前状态时17,那么下一个状态必须是18的后验条件等
例子:
@ThreadSafe
public final class Counter {
@GuardedBy("this") private long value = 0;
public synchronized long getValue() {
return value;
}
public synchronized long increment() {
if (value == Long.MAX_VALUE)
throw new IllegalStateException("counter overflow");
return ++value;
}
}
4.1.2 依赖状态的操作
For example, you cannot remove an item from an empty queue; a queue must be in the "nonempty" state before you can remove an element.
4.1.3 状态所有权
比如,servlet中的ServletContext中的对象通过setAttribut和getAttribut不需要同步,但其状态所有权属于应用程序,由应用程序来控制同步
4.2 实例封闭
将数据封装在对象内部,对象的方位只限制在对象的方法上,从而更容易确保线程在访问数据时总能持有争取的锁
4.2.1 java监视器模式
即:synchronized ,Vector 和Hashtable
4.2.2 示例:车辆追踪
在车辆很多的情况下,如果需要一致的快照则是优点。如果需要实时的最新信息,则是缺点
4.3 线程安全性的委托
4.3.1 示例:基于委托的车辆追踪器
使用不变的point类
将线程安全委托给ConcurrentHashMap
4.3.2 独立的状态变量
例子:VisualComponent keyListeners 和 mouseListeners 中两个链表都是线程安全的,且没有耦合。所以VisualComponent把安全委托给两个安全的链表是可以的
4.3.3 当委托无效时
例子:NumberRange 中 AtomicInteger lower 和 AtomicInteger upper,虽然是线程安全的类。但彼此有耦合关系,此时需要加锁机制处理
4.3.4 发布底层的状态变量
如果Counter类中,value有约束不能为负数,发布value则会这个类出错
VisualComponent中keyListeners 和 mouseListeners 时安全的,是可以发布的
4.3.5 示例:发布状态可变的车辆追踪器
(已在开头重点描述)
4.4 在现有安全类中添加功能
例如,给一个安全的列表添加原子操作(put-if-Absent)
方法1:最安全的方式是修改源代码,但这通常无法做到
方法2:扩张extends,但比直接修改源代码脆弱。因为同步策略实现被分布到多个单独维护的线程中
方法3:扩展类的功能(4.4.1 客户端加锁例子)
方法4:组合(更好的方式,4.4.2介绍)
4.4.1 客户端加锁机制
对于 被Collections.synchronizedLis的ArrayList,以上方法都不行,以为客户端并不知道ArrayList的class,第三种方法,扩展类的功能,放在一个辅助类中
@NotThreadSafe
public class ListHelper<E> {
public List<E> list =
Collections.synchronizedList(new ArrayList<E>());
...
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
但是如上方法,在错误的锁上进行了同步,应为
@ThreadSafe
public class ListHelper<E> {
public List<E> list =
Collections.synchronizedList(new ArrayList<E>());
...
public boolean putIfAbsent(E x) {
synchronized (list) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
}
4.4.2 Composition
Listing 4.16. Implementing Put-if-absent Using Composition.
@ThreadSafe
public class ImprovedList<T> implements List<T> {
private final List<T> list;
public ImprovedList(List<T> list) { this.list = list; }
public synchronized boolean putIfAbsent(T x) {
boolean contains = list.contains(x);
if (contains)
list.add(x);
return !contains;
}
public synchronized void clear() { list.clear(); }
// ... similarly delegate other List methods
}
4.5 将同步策略文档化
Document a class's thread safety guarantees for its clients; 向客户端说明类的线程安全性保证的
document its synchonization policy for its maintainers. 向维护人员提供同步策略
相关推荐
《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括线程安全类设计、实例封闭、线程安全性委托、现有线程安全类中添加功能和文档化同步策略等内容
第4章 对象的组合 4.1 设计线程安全的类 4.1.1 收集同步需求 4.1.2 依赖状态的操作 4.1.3 状态的所有权 4.2 实例封闭 4.2.1 Java监视器模式 4.2.2 示例:车辆追踪 4.3 线程安全性的委托 4.3.1 示例:基于...
《Java并发编程实战》是一本深入探讨Java平台并发编程的权威指南,由Tim Peierls等人与Brian Goetz合著,旨在帮助Java开发者理解和掌握在多线程环境中编写高效、安全的代码。这本书由拥有丰富经验的JDK并发大师及...
《Java并发编程实践》是Java开发者深入理解并发编程的重要参考资料,尤其对于想要提升多线程应用设计和性能优化技能的程序员来说,这本书提供了丰富的实践经验和深入的理论知识。以下是根据提供的章节内容概述的一些...
Java并发编程实战,第1章 简介,第2章 线程安全性 第3章 对象的共享 第4章 对象的组合 第5章 基础构建模块 第6章 任务执行 第7章 取消与关闭 第8章 线程池的使用 第9章 图形用户界面应用程序 第10章 避免...
第4章 对象的组合 4.1 设计线程安全的类 4.1.1 收集同步需求 4.1.2 依赖状态的操作 4.1.3 状态的所有权 4.2 实例封闭 4.2.1 Java监视器模式 4.2.2 示例:车辆追踪 4.3 线程安全性的委托 4.3.1 示例:基于...
#### 四、Java并发编程最佳实践 ##### 4.1 减少锁的竞争 在设计并发程序时,应尽可能减少锁的使用范围和时间,避免过度锁定。例如,可以通过分解任务、优化数据结构等方式减少锁的竞争。 ##### 4.2 使用线程本地...
通过学习《Java并发实战编程》,读者不仅可以掌握Java并发编程的基本原理,还能学习到实际开发中的最佳实践,从而编写出更加健壮、高效的并发代码。这本书对于提升Java开发者在并发领域的专业素养具有重要意义。
│ Java并发编程.png │ ppt+源码.rar │ 高并发编程第二阶段01讲、课程大纲及主要内容介绍.wmv │ 高并发编程第二阶段02讲、介绍四种Singleton方式的优缺点在多线程情况下.wmv │ 高并发编程第二阶段03讲、...
龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四...
第4章 组合对象 4.1 设计线程安全的类 4.2 实例限制 4.3 委托线程安全 4.4 向已有的线程安全类添加功能 4.5 同步策略的文档化 第5章 构建块 5.1 同步容器 5.2 发容器 5.3 阻塞队列和生产者一消费者模式 5.4 阻塞...
第1章介绍Java并发编程的挑战,向读者说明进入并发编程的世界可能会遇到哪些问题,以及如何解决。 第2章介绍Java并发编程的底层实现原理,介绍在CPU和JVM这个层面是如何帮助Java实现并发编程的。 第3章介绍深入介绍...
这本书涵盖了Java并发编程的核心概念和技术,旨在帮助开发者在实际项目中高效地处理高并发场景。随书附带的代码提供了丰富的示例,以便读者能够更直观地理解并实践这些理论知识。 1. **Java并发基础** - **线程与...
《Java并发编程实战1》这本书深入探讨了Java平台上的并发编程技术。本书分为四个部分,旨在帮助读者理解和掌握如何在实际应用中有效地利用多线程。 第一部分的基础知识介绍了线程的基本概念。线程是程序执行的最小...
在线程获取锁时会调用AQS的acquire()方法,该方法第一次尝试获取锁如果失败,会将该线程加入到CLH队列中:public final void acqui