Java 并发编程(三)为线程安全类中加入新的原子操作
Java 类库中包括很多实用的”基础模块“类。通常,我们应该优先选择重用这些现有的类而不是创建新的类。:重用能减少开发工作量、开发风险(由于现有类都已经通过測试)以及维护成本。有时候,某个线程安全类能支持我们须要的全部操作,但很多其它的时候,现有的类仅仅能支持大部分的操作,此时就须要在不破坏线程安全的情况下加入一个新的操作。
如果我们须要一个线程安全的链表,他须要提供一个原子的”若没有则加入(Put-If-Absent)“的操作。同步的 List 类已经实现了大部分的功能,我们能够依据它提供的 contains 方法和 add 方法构造一个实现。
能够有四种方法来实现这个原子操作。
第一种方法,也是最安全的方法,便是改动原始类。
但这一般是无法做到的,由于你可能无法訪问或改动类的源码。要想改动原始的类,就须要深刻理解代码中的同步策略,这样添加的功能才干与原有的设计保持一致。假设直接将新方法加入到类中,那么意味着实现同步策略的全部代码仍然处于一个源码文件里,从而更easy理解与维护。
另外一种方法,能够扩展(继承)这个类—-假如原始类在设计的时候时考虑到了它的可扩展性。
比如,我们能够设计一个 BetterVector 对 Vector 进行扩展,并加入了一个新方法 putIfAbsent。
public class BetterVector<E> extends Vector<E>{
public synchronized boolean putIfAbsent(E x){
boolean absent = !contains(x);
if(absent)add(x);
return absent;
}
}
扩展 Vector 非常easy,但并不是全部的类都像 Vector 那样将状态向子类公开,因此也就不适合採用这样的方法。
”扩展“方法比較脆弱,主要原因是 同步策略的实现被分离到了多个源码文件里,假设底层类改变了同步策略,更改了不同的锁来保护状态,那么子类便会被破坏。
第三种方法,使用辅助类,实现client加锁机制。
对于某些类,比方 Collections.synchronizedList 封装的 ArrayList , 前两种方法都行不通,由于客户代码不知道在同步封装器工厂方法中返回的 List 对象的类型。这时候採用client加锁的方式,将扩展代码放到一个”辅助类“中。
于是我们非常自然的就写出 ListHelper 辅助类。
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;
}
}
咋一看没问题,但是非常遗憾,这样的方式是错误的。
尽管 putIfAbsent 已经声明为 synchronized ,可是它却是在 ListHelper 上加锁,而 List 却是用自己或内部对象的锁。 ListHelper 仅仅是带来了同步的假象,
在 Vector 和同步封装器类的文档中指出,他们是通过 Vector 或封装容器内部锁来支持client加锁。以下我们给出正确的client加锁。
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;}
}
}
通过加入一个原子操作来扩展类是脆弱的,由于它将类的加锁代码分布到多个类中。然而,client加锁却更加脆弱,由于它将类的加锁代码放到与其全然无关的其它类中。
第四种方法,使用组合(Composition)的方式。
public class ImprovedList<T> implements List<T> {
public final List<T> list;
public ImprovedList(List<T> list) {
this.list = list;
}
public synchronized boolean putIfAbsent(T x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
//...依照相似的方式托付List的其它方法
}
ImprovedList 通过自身的内置锁添加了一层额外的加锁。它并不关心底层的 List 是否是线程安全的,即使 List 不是线程安全的或者改动了它的枷锁方式,Improved 也会提供一致的加锁机制来实现线程安全性。尽管额外的同步层可能导致轻微的性能损失,但与模拟还有一个对象的加锁策略相比,ImprovedList 更为健壮。其实,我们使用了 Java 监视器模式来封装现有 List ,而且仅仅要在类中拥有指向底层 List 的为意外不引用,就能确保线程安全性。
Java 并发编程(三)为线程安全类中加入新的原子操作的更多相关文章
- Java并发编程系列-(2) 线程的并发工具类
2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...
- JAVA并发-为现有的线程安全类添加原子方法
JAVA中有许多线程安全的基础模块类,一般情况下,这些基础模块类能满足我们需要的所有操作,但更多时候,他们并不能满足我们所有的需要.此时,我们需要想办法在不破坏已有的线程安全类的基础上添加一个新的原子 ...
- Java并发编程:进程和线程的由来(转)
Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程.当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够融会贯通 ...
- Java并发编程三个性质:原子性、可见性、有序性
并发编程 并发程序要正确地执行,必须要保证其具备原子性.可见性以及有序性:只要有一个没有被保证,就有可能会导致程序运行不正确 线程不安全在编译.测试甚至上线使用时,并不一定能发现,因为受到当时的 ...
- 【Java并发编程六】线程池
一.概述 在执行并发任务时,我们可以把任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程,只要池里有空闲的线程,任务就会分配一个线程执行.在线程池的内部,任务被插入一个阻塞队列(Blo ...
- 【Java并发编程一】线程安全和共享对象
一.什么是线程安全 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用代码代码不必作其他的协调,这个类的行为仍然是正确的,那么称这个类是线程安全的 ...
- Java并发编程扩展(线程通信、线程池)
之前我说过,实现多线程的方式有4种,但是之前的文章中,我只介绍了两种,那么下面这两种,可以了解了解,不懂没关系. 之前的文章-->Java并发编程之多线程 使用ExecutorService.C ...
- 【java并发编程实战】-----线程基本概念
学习Java并发已经有一个多月了,感觉有些东西学习一会儿了就会忘记,做了一些笔记但是不系统,对于Java并发这么大的"系统",需要自己好好总结.整理才能征服它.希望同仁们一起来学习 ...
- 【Java并发编程三】闭锁
1.什么是闭锁? 闭锁(latch)是一种Synchronizer(Synchronizer:是一个对象,它根据本身的状态调节线程的控制流.常见类型的Synchronizer包括信号量.关卡和闭锁). ...
随机推荐
- 【Cloud Foundry】Could Foundry学习(二)——核心组件分析
在阅读的过程中有不论什么问题,欢迎一起交流 邮箱:1494713801@qq.com QQ:1494713801 Cloud Foundry核心组件架构图例如以下: 主要组件: Clou ...
- Disable root 账号登陆
1. 改动文件: /etc/ssh/sshd_config #PermitRootLogin yes 改动为: PermitRootLogin no 2. 重新启动ssh进程 /etc/init.d/ ...
- 编写生成彩色验证码的Servlet
利用Ajax实现无刷新的彩色验证码时,也需要编写一个名称为PictureCheckCode.java的Servlet,该类继承HttpServlet,主要通过service()方法生成验证码. 下面将 ...
- 8592 KMP算法
8592 KMP算法 时间限制:1000MS 内存限制:1000K 题型: 编程题 语言: 无限制 描写叙述 用KMP算法对主串和模式串进行模式匹配. 本题目给出部分代码.请补全内容. #inc ...
- openssl之BIO系列之5---CallBack函数及其控制
CallBack函数及其控制 ---依据openssl doc/crypto/bio/bio_set_callback.pod翻译和自己的理解写成 (作者:DragonKin ...
- 初窥Linux 之 区分硬连接和软连接
一.Linux下的两种连接文件及创建方式 在Linux下面的连接文件有两种——软连接和硬连接,虽然都是连接文件,但两者却有很大的区别.一种是类似于Windows的快捷方式功能的文件(或目录),这种连接 ...
- Chrome App远程控制
現在google app連上chrome就能遠控了出了幾年了, 能用觸控控制mouse https://chrome.google.com/webstore/detail/chrome-remote- ...
- hdu 1007 最近点对问题(Splay解法)
为什么要写这个题..经典啊,当然,别以为我用分治做的,不过主要思想还是那神奇的六个点共存(一个h*2h的矩形中最多能放下多少个点使得两两距离不超过h) 其实我是在这里看到的 http://commun ...
- POJ 3076 Sudoku (dancing links)
题目大意: 16*16的数独. 思路分析: 多说无益. 想说的就是dancing links 的行是依照 第一行第一列填 1 第一行第二列填 2 -- 第一行第十五列填15 第一行第二列填 1 -- ...
- JAVA WEB开发环境搭建教程
一.下载安装JDK,配置好环境变量.(例如我JDK安装的目录为:C:\Program Files (x86)\Java\jdk1.6.0_10 ) 点击我的电脑-属性-系统设置(高级系统设置) ...