JAVA并发-为现有的线程安全类添加原子方法
JAVA中有许多线程安全的基础模块类,一般情况下,这些基础模块类能满足我们需要的所有操作,但更多时候,他们并不能满足我们所有的需要。此时,我们需要想办法在不破坏已有的线程安全类的基础上添加一个新的原子操作。有如下4中方案:
1 修改类的源码,以添加新的原子操作
2 继承该线程安全类,并添加原子操作
3 使用客户端加锁方式
4 使用组合方式(推荐)
一般来讲,修改源码的方式不太可行,这样会破坏原有类的封装性而且有些时候,源码不可获得。我们从第二种方式开始举例:
假设现在对于类Vector,我们知道它是线程安全类。如果想为他添加一个“若没有则添加”方法,可是如下进行:
- public class ImprovedVector<T> extends Vector<T>{
- public synchronized boolean putIfAbsent(T x){
- boolean flag=contains(x);
- if(!flag)
- add(x);
- return !flag;
- }
- }
我们来分析上面的代码:使用ImprovedVector类对象的内置锁,保证了contains()和add()方法的原子性,由于ImprovedVector类对象的内置锁也就是Vector类对象的内置锁(即add()方法和contains()方法的锁),这样有保证了add()方法和contains()方法的可见性,可以达到预期效果。
第三种方法举例:
- (错误的实现)
- public class ImprovedList<T>{
- public List<T> list=Collections.synchronizedList(new ArrayList<T>());
- public synchronized boolean putIfAbsent(T x){
- boolean flag=list.contains(x);
- if(!flag)
- list.add(x);
- return !flag;
- }
- }
上面的例子是一个错误的例子,我们来分析下:首先,synchronized保证了list.contains()方法和list.add()方法的原子性,假设现在有一个类对象在执行putIfAbsent()方法,而且即将执行(还没执行)list.add(2)方法,此时,有另外一个线程抢先执行了list.add(2)方法,该线程执行完毕之后,释放了list的锁,接着即将执行(还没执行)list.add(2)方法开始得到CPU并执行。瞧,这个过程中,数字2被添加了2次。就是说,上面的代码中仅仅保证了contains()方法和add()方法的原子性,以及对对list引用操作的互斥性,并没有保证list.add()方法的可见性。
仔细想想,问题出在putIfAbsent()方法的锁与list对象的锁不是同一个,putIfAbsent()方法的锁是ImprovedList类的锁,而list.add()方法的锁是Collections.synchronizedList()使用的锁,因此将上面的代码改成:
- (正确的实现)
- public class ImprovedList<T>{
- public List<T> list=Collections.synchronizedList(new ArrayList<T>());
- public synchronized boolean putIfAbsent(T x){
- synchronized(list){
- boolean flag=list.contains(x);
- if(!flag)
- list.add(x);
- return !flag;
- }
- }
- }
第四种方法举例:
- 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 flag=list.contains(x);
- if(!flag)
- list.add(x);
- return !flag;
- }
- }
- ...实现List<T>接口中的其他方法
- }
乍一看发现,上面的代码在安全性方面好像弱了好多, putIfAbsent(T x)方法中的fianl变量list可能连线程安全类都不是,但是对于上面的代码,我们有个假设(当某个链表对象在传递给ImprovedList的构造函数之后,客户代码再也不会使用这个对象,而是使用与其对应的ImprovedList对象),有了这个假设前提,上面的代码就是线程安全的了。
上面就是JAVA并发编程中,在一个已有的线程安全类的基础上添加同步函数的4个方法。
JAVA并发-为现有的线程安全类添加原子方法的更多相关文章
- Java并发编程实战 之 线程安全性
1.什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何种调用方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全 ...
- Java并发编程系列-(2) 线程的并发工具类
2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...
- Java并发编程(十三)在现有的线程安全类中添加功能
重用现有的类而不是创建新的类,可以降低工作量,开发风险以及维护成本. 有时候线程安全类可以支持我们所有的操作,但更多时候,现有的了类只能支持大部分的操作,此时就需要在不破坏线程安全性的情况下添加一个新 ...
- Java并发编程学习:线程安全与锁优化
本文参考<深入理解java虚拟机第二版> 一.什么是线程安全? 这里我借<Java Concurrency In Practice>里面的话:当多个线程访问一个对象,如果不考虑 ...
- java并发编程实战之线程安全性(一)
1.1什么是线程安全性 要对线程安全性给出一个确切的定义是非常复杂的.最核心的概念就是正确性.正确性:某个类的行为与其规范完全一致.在良好的规范中通常会定义各种不变性条件来约束对象的状态,以及定义各种 ...
- Java并发:五种线程安全类型、线程安全的实现、枚举类型
1. Java中的线程安全 Java线程安全:狭义地认为是多线程之间共享数据的访问. Java语言中各种操作共享的数据有5种类型:不可变.绝对线程安全.相对线程安全.线程兼容.线程独立 ① 不可变 不 ...
- Java并发(一)——线程安全的容器(上)
Java中线程安全的容器主要包括两类: Vector.Hashtable,以及封装器类Collections.synchronizedList和Collections.synchronizedMap: ...
- Java并发(三)线程池原理
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 1. 降低资源消耗.通过重复利用已创建的线程降低线程 ...
- 【Java并发编程六】线程池
一.概述 在执行并发任务时,我们可以把任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程,只要池里有空闲的线程,任务就会分配一个线程执行.在线程池的内部,任务被插入一个阻塞队列(Blo ...
随机推荐
- [CTSC2016]单调上升路径
题目:UOJ#201. 题目大意:给定n个点(n是偶数)的完全图,现在要你给每条边确定一个权值(互不相等),使得最长的单调上升路径最短.现在要你输出边的权值. 一条路径被称为单调上升的,如果沿着它走时 ...
- 【转载】spring-boot 项目跳转到JSP页面
原路径:https://blog.csdn.net/qq_36820717/article/details/80008225 1.新建spring-boot项目 目录结构如下 2.新建TestCon ...
- Union File System
目录 Union File System AUFS Docker是如何使用AUFS的 image layer 和 AUFS (docker版本不同可能会有区别,我的是在/var/lib/docker下 ...
- 【BZOJ 1207】[HNOI2004]打鼹鼠
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 时间是按顺序的. 所以就有单调性啦. 写个DP就好. 设f[i]表示打第i只鼹鼠,最多能打几只鼹鼠. 则如果i和j的距离不超过它们的 ...
- Java 8 Stream API具体解释
Java 8 Stream API具体解释 一.Stream API介绍 Java 8引入了全新的Stream API,此Stream与Java I/O包里的InputStream和OutputStr ...
- [React Native] Dismiss the Keyboard in React Native
In this lesson you will learn how create a re-usable component that gives the user the ability to di ...
- iOS AES的加密解密
主要是要得到加密后的原来的字符创,MD5无法得到原有的,仅仅能用AES+base64 要用GTMBase64这个能够自己去下载, 主要看另外2个类文件. #import <Foundation/ ...
- rpm -qf 的使用技巧,以及怎样查找软件包
首先查看安装的软件包,或者时候安装有某某软件包的命令 rpm (-qa)| grep 软件名 root@mode oldboy]# rpm -qa| grep openssl openssl-dev ...
- Android中的消息机制
在分析Android消息机制之前.我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListen ...
- angularjs 工具方法
<!DOCTYPE HTML> <html ng-app> <head> <meta http-equiv="Content-Type" ...