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. 4----COM:a Generative Model for group recommendation(组推荐的一种生成模型)

    1.摘要: 组推荐的一个挑战性问题:因为不同组的成员就有不同的偏好,如何平衡这些组员的偏好是一个难以解决的问题. 在本文中,作者提出了一个COM的概率模型来建立组活动生成过程. 直觉上: 一个组中的用 ...

  2. IFC2x3标准阅读

    参考地址:西北逍遥-IFC数据模式架构的四个概念层详解说明 1.架构图 IFC模型体系结构由四个层次构成,从下到上依次是 资源层(Resource Layer).核心层(Core Layer).交互层 ...

  3. xampp、phpstudy安装phalcon

    1.下载扩展 https://github.com/phalcon/cphalcon/releases/tag/v3.4.1选择PHP对应版本的phalcon扩展 2.PHP.ini 配置phalco ...

  4. Python学习笔记(二):字符串类型

    在上一篇随笔(https://www.cnblogs.com/g-qiang/p/10448813.html)中,说到 Python 有六种标准数据类型,而数字类型和字符串类型又是其中基本的数据类型. ...

  5. 【codeforces 404D】Minesweeper 1D

    [题目链接]:http://codeforces.com/problemset/problem/404/D [题意] 让你玩一个1维的扫雷游戏; 游戏的描述由数字0..2以及符号*表示; 分别表示这个 ...

  6. ubuntu下不用拔盘就可以重新识别usb设备

    #!/bin/sh # Usage: ./resetusb ARGUMENT(The keyword for your usb device) var1=$ keyword=${var1:=Stora ...

  7. Redis Java调用

    Redis Java调用 package com.stono.redis; import redis.clients.jedis.Jedis; public class RedisJava { pub ...

  8. HDU 2196 Computer(求树上每一个节点到其他点的最远距离)

    解题思路: 求出树的直径的两个端点.则树上每一个节点到其它点的最远距离一定是到这两个端点的距离中最长的那一个. #include <iostream> #include <cstri ...

  9. 主程的晋升攻略(4):TCP、消息分包和协议设计

    在<主程的晋升攻略(3):IP.DNS和CDN>中,一次网络请求经过DNS解析知道了目的IP,如今就要发出网络包,这里我们说一说TCP的相关话题. TCP是一种流式协议 讲网络编程的教科书 ...

  10. [PHP]怎样在SAE的CodeIgniter项目中隐藏掉index.php

    第一步:改动项目根文件夹的config.yaml文件.加入例如以下内容: handle: - rewrite: if(!is_dir() && !is_file() && ...