从SynchronizedCollection说起
SynchronizedCollection简介
SynchronizedCollection是Collections下所有现场安全集合的父类,并发安全集合可以分为三类,一种是比较老的实现,例如vector,一种是concurrent包下的,另外一种就是SynchronizedCollection。他是通过对并发不安全的集合进行包装,使得线程安全。
实现介绍
上面也说了,他是通过对并发不安全的集合进程包装的。思想上就是加了一个object锁,然后在每个方法上加入锁的逻辑。
static class SynchronizedCollectionimplements Collection, Serializable {
final Collectionc; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collectionc, Object mutex) {
this.c = Objects.requireNonNull(c);
this.mutex = Objects.requireNonNull(mutex);
}
....
}
SynchronizedCollection有两个成员变量,一个是被包装的集合对象c,一个是用来作为锁的对象mutex。集合对象和锁对象都可以通过构造函数传入。
大家如果看过vector的实现的话,大概明白接下来的实现手段了,就是加锁!
public int size() {
synchronized (mutex) {return c.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return c.isEmpty();}
}
public boolean contains(Object o) {
synchronized (mutex) {return c.contains(o);}
}
public Object[] toArray() {
synchronized (mutex) {return c.toArray();}
}
publicT[] toArray(T[] a) {
synchronized (mutex) {return c.toArray(a);}
}
是不是看了实现的你也是微微一下,这里就是通过synchronized 加锁mutex。然后在代码块里执行原来的集合的方法,由于每个方法都被加上了锁,所以集合也成为了并发安全的集合。
安全性依靠的就是集合被覆写,如果没有被覆写,那么线程安全性也就不保证了。很多人也很好奇,这都是包装安全了,为啥还会这么说。
下面就展示一个例外。
public Iteratoriterator() {
return c.iterator(); // Must be manually synched by user!
}
iterator方法没有被覆写!也就是说使用迭代器迭代的时候,线程是不安全的,所以这个方法的也加了注释,需要用户加同步。
很多时候大家的第一反应就是给iterator加synchronized 的就可以。但是真的是这样吗?
我们来推演一下,如果在iterator方法上加锁,那么确实保证了iterator方法是线程安全的,在并发的情况下,只会有一个线程操作成功,成功获取到iterator对象。但是接下来呢,我们要拿到iterator对象进行迭代,此时如果集合有增删操作是可以的,因为iterator对象的操作完全没有锁竞争。此时迭代就会出错。
装饰者模式
SynchronizedCollection这种通过实现Collection接口,然后自己把接口的方法都实现了一遍,并且使用了组合的方式,对每个方法增加了新的功能,最后还返回接口对象,面向接口编程。这就是典型的装饰者模式。
为了增加新的功能,不断的继承接口,实现自己的新逻辑,而且对调用端屏蔽了操作。如果说在增加一个需求,就是需要一个集合操作完之后就删除一个元素。那么只要实现Collection接口,对每个方法的调用都做一个删除的操作,并且执行传入的集合对象就可以了,在生成对象的时候,不断的构造对象传入。如果功能不需要了,例如不需要线程安全的需求了,那么只要在生成对象的时候,直接去掉SynchronizedCollection就可以了。
装饰者模式的优点就是对外面向接口,对调用者屏蔽细节。然后功能拆分比较细,可以进行比较灵活的组装。
iterator的问题,其实也是装饰者模式的弊端,就是他对包装者的功能扩展是有一定限制的。他可以对他包装的对象进行扩展,但是并不能对包装对象里依赖的对象进行太多的干预。
java中装饰者模式的应用
除了上面的SynchronizedCollection。java中另外一个典型的案例就是io。
java的io是可以不停的功能扩展的。
FileInputStream是基础的字节io
InputStreamReader可以对FileInputStream进行功能扩充
BufferedReader对Reader可以再进程扩充
其实大家使用的时候的一些缺点也暴露出来了,就是功能扩充的限制问题,我们一旦装饰到BufferedReader,就不会再去使用read方法了,主要使用readLine这个功能了,其实接口并没有这个方法,所以我们使用io的时候已经不再是面向接口了,更多的是面向了具体的实现,因为在文件的场景下。接口并不能彻底抽象全部功能,他只能有共有的。
总结
java中有两个地方使用了装饰者模式,io,SynchronizedCollection。
SynchronizedCollection对每个方法进行了功能的扩充,但是对包装类里的成员变量不能控制。
io增加了功能,但是功能有更多的特性,最后反而是面向了实现类编程。
装饰者模式的好处是灵活,功能扩展方便。坏处就是功能扩展有限制,一个限制在接口,一个限制在被装饰者内部的成员变量。
从SynchronizedCollection说起的更多相关文章
- 用Collections.synchronizedCollection创建线程安全的集合、列表。。。
Collection c=Collections.synchronizedCollection(new ArrayList()); List list=Collections.synchronized ...
- java - 并发集合 Vector、synchronizedCollection、CopyOnWriteArrayList之间的区别。
概要 JDK中提供ArrayList集合方便我们对集合内元素进行增删改查,但是ArrayList为了能够在单线程中快速进行操作其设计并不支持多线程进行操作.ArrayList在多线程环境下可能会产生j ...
- 计算机程序的思维逻辑 (54) - 剖析Collections - 设计模式
上节我们提到,类Collections中大概有两类功能,第一类是对容器接口对象进行操作,第二类是返回一个容器接口对象,上节我们介绍了第一类,本节我们介绍第二类. 第二类方法大概可以分为两组: 接受其他 ...
- Collection集合
一些关于集合内部算法可以查阅这篇文章<容器类总结>. (Abstract+) Collection 子类:List,Queue,Set 增: add(E):boolean addAll(C ...
- C#泛型方法解析
C#2.0引入了泛型这个特性,由于泛型的引入,在一定程度上极大的增强了C#的生命力,可以完成C#1.0时需要编写复杂代码才可以完成的一些功能.但是作为开发者,对于泛型可谓是又爱又恨,爱的是其强大的功能 ...
- Java Hashtable的实现
先附源码: package java.util; import java.io.*; /** * This class implements a hash table, which maps keys ...
- Java基础应用
Java集合类解析 List.Map.Set三个接口,存取元素时,各有什么特点? List 以特定次序来持有元素,可有重复元素.Set 无法拥有重复元素,内部排序.Map 保存key-value值,v ...
- Java集合面试题
1.Java集合框架是什么?说出一些集合框架的优点? 每种编程语言中都有集合,最初的Java版本包含几种集合类:Vector.Stack.HashTable和Array.随着集合的广泛使用,Java1 ...
- System.Collections.Generic的各容器类的用法
演示System.Collections.Generic的各容器类的用法. 包括:Dictionary,KeyValuePair,SortedDic tionary,SortedList,HashSe ...
随机推荐
- (0.2.5)Mysql安装——RPM方式安装
rpm安装mysql 卸载与安装服务端 一.安装服务端与客户端 #查看RPM包中所有的文件shell> rpm -qpl mysql-community-server-version-dis ...
- 使用curl发送post或者get数据
一. 使用curl可以仿造http的请求,向目标服务器或者是目标IP发送数据,进行操作. (1).使用php操作curl向某个接口上发送GET请求: 下面是写的一个比较简单的请求方式请求数据,传入的参 ...
- C的指针疑惑:C和指针6(指针)
NULL: 对所有指针变量进行显式的初始化是种好事:(1)如果你知道指针将被初始化为什么地址,就直接初始化该地址, (2)否则把它初始化位NULL. 注意:假定变量a存储于位置100. × = 看上去 ...
- 使用stringstream格式化字符串
stringstream所在头文件为<sstream> 一般有如下常用功能: 1.安全格式化字符串 stringstream常用来安全的格式化若干个字符串,数值到一个缓冲区, 而不用担心溢 ...
- C++学习笔记-操作符重载
操作符重载(operator overloading)是一种形式的C++多态,C++将操作符重载扩展到用户自定义的类型,如允许使用+将两个自定义的对象相加,编译器将根据操作数的数目和类型决定使用那种加 ...
- VMware+CentOS7+jdk1.7+hadoop2.4.1
1.工具 CentOS7:去官网下载,然后找到阿里的镜像,DVD版本就好,4个G大小https://www.centos.org/download/ vmware:去官网下载最新版本 2.要点 先装V ...
- 新式转型操作符[条款9] --《C++必知必会》
在旧式转型(cast)下面隐藏着一些见不得人的.鬼鬼祟祟的东西.他们的语法形式使其在一段代码中通常很难引起人们的注意,但它们可能会搞一些可怕的破坏活动,就好比你冷不丁被一个恶棍猛击一拳似的.让我们阐明 ...
- 160726 smarty 笔记(2)
<?php //取当前页 $p=1; if(!empty($_GET["page"])) { $p=$_GET["page"]; } //定义页面缓存文件 ...
- JavaScript:学习笔记(8)——对象扩展运算符
JavaScript:学习笔记(8)——扩展运算符 对象的扩展运算符 扩展运算符是三个点(...).用于取出参数对象的所有可遍历属性,然后拷贝到当前对象之中. 如上图所示,新建了一个对象a,然后通过扩 ...
- javaScript动画3 事件对象event onmousemove
事件对象的获取(event的获取) var event = event || window.event;(主要用这种) screenX.pageX和clientX的区别 PageY/pageX: 鼠标 ...