JAVA中有许多线程安全的基础模块类,一般情况下,这些基础模块类能满足我们需要的所有操作,但更多时候,他们并不能满足我们所有的需要。此时,我们需要想办法在不破坏已有的线程安全类的基础上添加一个新的原子操作。有如下4中方案: 
1 修改类的源码,以添加新的原子操作 
2 继承该线程安全类,并添加原子操作 
3 使用客户端加锁方式 
4 使用组合方式(推荐) 
一般来讲,修改源码的方式不太可行,这样会破坏原有类的封装性而且有些时候,源码不可获得。我们从第二种方式开始举例:

假设现在对于类Vector,我们知道它是线程安全类。如果想为他添加一个“若没有则添加”方法,可是如下进行:

  1. public class ImprovedVector<T> extends Vector<T>{
  2. public synchronized boolean putIfAbsent(T x){
  3. boolean flag=contains(x);
  4. if(!flag)
  5. add(x);
  6. return !flag;
  7. }
  8. }

我们来分析上面的代码:使用ImprovedVector类对象的内置锁,保证了contains()和add()方法的原子性,由于ImprovedVector类对象的内置锁也就是Vector类对象的内置锁(即add()方法和contains()方法的锁),这样有保证了add()方法和contains()方法的可见性,可以达到预期效果。

第三种方法举例:

  1. (错误的实现)
  2. public class ImprovedList<T>{
  3. public List<T> list=Collections.synchronizedList(new ArrayList<T>());
  4. public synchronized boolean putIfAbsent(T x){
  5. boolean flag=list.contains(x);
  6. if(!flag)
  7. list.add(x);
  8. return !flag;
  9. }
  10. }

上面的例子是一个错误的例子,我们来分析下:首先,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()使用的锁,因此将上面的代码改成:

  1. (正确的实现)
  2. public class ImprovedList<T>{
  3. public List<T> list=Collections.synchronizedList(new ArrayList<T>());
  4. public synchronized boolean putIfAbsent(T x){
  5. synchronized(list){
  6. boolean flag=list.contains(x);
  7. if(!flag)
  8. list.add(x);
  9. return !flag;
  10. }
  11. }
  12. }

第四种方法举例:

  1. public class ImprovedList<T> implements List<T>{
  2. private final List<T> list;
  3. public ImprovedList(List<T> list){
  4. this.list=list;
  5. }
  6. public synchronized boolean putIfAbsent(T x){
  7. boolean flag=list.contains(x);
  8. if(!flag)
  9. list.add(x);
  10. return !flag;
  11. }
  12. }
  13. ...实现List<T>接口中的其他方法
  14. }

乍一看发现,上面的代码在安全性方面好像弱了好多, putIfAbsent(T x)方法中的fianl变量list可能连线程安全类都不是,但是对于上面的代码,我们有个假设(当某个链表对象在传递给ImprovedList的构造函数之后,客户代码再也不会使用这个对象,而是使用与其对应的ImprovedList对象),有了这个假设前提,上面的代码就是线程安全的了。

上面就是JAVA并发编程中,在一个已有的线程安全类的基础上添加同步函数的4个方法。

JAVA并发-为现有的线程安全类添加原子方法的更多相关文章

  1. Java并发编程实战 之 线程安全性

    1.什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何种调用方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全 ...

  2. Java并发编程系列-(2) 线程的并发工具类

    2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...

  3. Java并发编程(十三)在现有的线程安全类中添加功能

    重用现有的类而不是创建新的类,可以降低工作量,开发风险以及维护成本. 有时候线程安全类可以支持我们所有的操作,但更多时候,现有的了类只能支持大部分的操作,此时就需要在不破坏线程安全性的情况下添加一个新 ...

  4. Java并发编程学习:线程安全与锁优化

    本文参考<深入理解java虚拟机第二版> 一.什么是线程安全? 这里我借<Java Concurrency In Practice>里面的话:当多个线程访问一个对象,如果不考虑 ...

  5. java并发编程实战之线程安全性(一)

    1.1什么是线程安全性 要对线程安全性给出一个确切的定义是非常复杂的.最核心的概念就是正确性.正确性:某个类的行为与其规范完全一致.在良好的规范中通常会定义各种不变性条件来约束对象的状态,以及定义各种 ...

  6. Java并发:五种线程安全类型、线程安全的实现、枚举类型

    1. Java中的线程安全 Java线程安全:狭义地认为是多线程之间共享数据的访问. Java语言中各种操作共享的数据有5种类型:不可变.绝对线程安全.相对线程安全.线程兼容.线程独立 ① 不可变 不 ...

  7. Java并发(一)——线程安全的容器(上)

    Java中线程安全的容器主要包括两类: Vector.Hashtable,以及封装器类Collections.synchronizedList和Collections.synchronizedMap: ...

  8. Java并发(三)线程池原理

    Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 1. 降低资源消耗.通过重复利用已创建的线程降低线程 ...

  9. 【Java并发编程六】线程池

    一.概述 在执行并发任务时,我们可以把任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程,只要池里有空闲的线程,任务就会分配一个线程执行.在线程池的内部,任务被插入一个阻塞队列(Blo ...

随机推荐

  1. luogu P2252 取石子游戏(威佐夫博弈)

    题意 题解 对于像我这种不知道威佐夫博弈的人来说,拿到题就开始打表了. 然而打完后并没有发现什么. 然后才知道是威佐夫博弈. 结论是当(int)((b-a)*((sqrt(5.0)+1.0)/2.0) ...

  2. 2018秋寒假作业4——PTA编辑总结1

    #include<stdio.h> #include<math.h> int main(void) { int n,i,j,p,m,ge,N,k; char op; ){ sc ...

  3. Linux初学习

    Linux Linux运行与关闭 Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 UNIX 的多用户.多任务.支持多线程和多 CPU 的操作系统. Linux ...

  4. Codeforces 558E 线段树处理字符串内排序

    给出长度为n的字符串,m个操作. 每一个操作有三个值 l,r,op. op==1,表示将字符串中[ l ,r ]的部分依照升序排列. op==0,表示将字符串中[ l ,r ]的部分依照降序排列. 输 ...

  5. python json及mysql——读取json文件存sql、数据库日期类型转换、终端操纵mysql及python codecs读取大文件问题

    preface: 近期帮师兄处理json文件,须要读到数据库里面,以备其兴许从数据库读取数据.数据是关于yelp站点里面的: https://github.com/Yelp/dataset-examp ...

  6. [Javascript] Simplify Creating Immutable Data Trees With Immer

    Immer is a tiny library that makes it possible to work with immutable data in JavaScript in a much m ...

  7. vim水平摆放全部窗体的三个方法

    Method1: map <F7> <ESC>:windo exe "normal \<foobar<C-W>C-W>K"<C ...

  8. Classes and functions

    As another example of a user-defined type, we’ll define a class called Time that records the time of ...

  9. Linux-php7安装redis

    Linux-php7安装redis 标签(空格分隔): 未分类 安装redis服务 1 下载redis cd /usr/local/ 进入安装目录 wget http://download.redis ...

  10. 使用U盘安装win8(win8.1)系统

    下载镜像 下载系统镜像,可以选32位或64位.下完后放到非系统盘(如果需要重新分区的,要先保存到移动硬盘或U盘) 使用U盘制作PE 准备一个4G以上的U盘(备份好U盘里的资料,因为刻录PE需要格式化U ...