Java并发(5):同步容器
一. 同步容器出现的原因
在Java的集合容器框架中,主要有四大类别:List、Set、Queue、Map。
List、Set、Queue接口分别继承了Collection接口,Map本身是一个接口。
注意Collection和Map是一个顶层接口,而List、Set、Queue则继承了Collection接口,分别代表数组、集合和队列这三大类容器。
像ArrayList、LinkedList都是实现了List接口,HashSet实现了Set接口,而Deque(双向队列,允许在队首、队尾进行入队和出队操作)继承了Queue接口,PriorityQueue实现了Queue接口。另外LinkedList(实际上是双向链表)实现了了Deque接口。
像ArrayList、LinkedList、HashMap这些容器都是非线程安全的。
如果有多个线程并发地访问这些容器时,就会出现问题。
因此,在编写程序时,必须要求程序员手动地在任何访问到这些容器的地方进行同步处理,这样导致在使用这些容器的时候非常地不方便。
所以,Java提供了同步容器供用户使用。
二. Java中的同步容器类
在Java中,同步容器主要包括2类:
1)Vector、Stack、HashTable
2)Collections类中提供的静态工厂方法创建的类
Vector实现了List接口,Vector实际上就是一个数组,和ArrayList类似,但是Vector中的方法都是synchronized方法,即进行了同步措施。
Stack也是一个同步容器,它的方法也用synchronized进行了同步,它实际上是继承于Vector类。
HashTable实现了Map接口,它和HashMap很相似,但是HashTable进行了同步处理,而HashMap没有。
Collections类是一个工具提供类,注意,它和Collection不同,Collection是一个顶层的接口。在Collections类中提供了大量的方法,比如对集合或者容器进行排序、查找等操作。最重要的是,在它里面提供了几个静态工厂方法来创建同步容器类,如下图所示:

三. 同步容器的缺陷
从同步容器的具体实现源码可知,同步容器中的方法采用了synchronized进行了同步,那么很显然,这必然会影响到执行性能,另外,同步容器就一定是真正地完全线程安全吗?不一定,这个在下面会讲到。
我们首先来看一下传统的非同步容器和同步容器的性能差异,我们以ArrayList和Vector为例:
1.性能问题
我们先通过一个例子看一下Vector和ArrayList在插入数据时性能上的差异:
public class Test {
public static void main(String[] args) throws InterruptedException {
ArrayList<Integer> list = new ArrayList<Integer>();
Vector<Integer> vector = new Vector<Integer>();
long start = System.currentTimeMillis();
for(int i=0;i<100000;i++)
list.add(i);
long end = System.currentTimeMillis();
System.out.println("ArrayList进行100000次插入操作耗时:"+(end-start)+"ms");
start = System.currentTimeMillis();
for(int i=0;i<100000;i++)
vector.add(i);
end = System.currentTimeMillis();
System.out.println("Vector进行100000次插入操作耗时:"+(end-start)+"ms");
}
}
这段代码上跑出来的结果是:
ArrayList进行100000次插入操作耗时:8ms
Vector进行100000次插入操作耗时:17ms
进行同样多的插入操作,Vector的耗时是ArrayList的两倍。这只是其中的一方面性能问题上的反映。
另外,由于Vector中的add方法和get方法都进行了同步,因此,在有多个线程进行访问时,如果多个线程都只是进行读取操作,那么每个时刻就只能有一个线程进行读取,其他线程便只能等待,这些线程必须竞争同一把锁。因此为了解决同步容器的性能问题,在Java 1.5中提供了并发容器,位于java.util.concurrent目录下。
2. 同步容器不一定安全
也有有人认为Vector中的方法都进行了同步处理,那么一定就是线程安全的,事实上这可不一定。看下面这段代码:
public class Test {
static Vector<Integer> vector = new Vector<Integer>();
public static void main(String[] args) throws InterruptedException {
while(true) {
for(int i=0;i<10;i++)
vector.add(i);
Thread thread1 = new Thread(){
public void run() {
for(int i=0;i<vector.size();i++)
vector.remove(i);
};
};
Thread thread2 = new Thread(){
public void run() {
for(int i=0;i<vector.size();i++)
vector.get(i);
};
};
thread1.start();
thread2.start();
while(Thread.activeCount()>10) {
}
}
}
}
运行结果为:

正如大家所看到的,这段代码报错了:数组下标越界。
也许有人会问:Vector是线程安全的,为什么还会报这个错?很简单,对于Vector,虽然能保证每一个时刻只能有一个线程访问它,但是不排除这种可能:
当某个线程在某个时刻执行这句时:
for(int i=0;i<vector.size();i++)
vector.get(i);
假若此时vector的size方法返回的是10,i的值为9
然后另外一个线程执行了这句:
for(int i=0;i<vector.size();i++)
vector.remove(i);
将下标为9的元素删除了。
那么通过get方法访问下标为9的元素肯定就会出问题了。
因此为了保证线程安全,必须在方法调用端做额外的同步措施,如下面所示:
public class Test {
static Vector<Integer> vector = new Vector<Integer>();
public static void main(String[] args) throws InterruptedException {
while(true) {
for(int i=0;i<10;i++)
vector.add(i);
Thread thread1 = new Thread(){
public void run() {
synchronized (Test.class) { //进行额外的同步
for(int i=0;i<vector.size();i++)
vector.remove(i);
}
};
};
Thread thread2 = new Thread(){
public void run() {
synchronized (Test.class) {
for(int i=0;i<vector.size();i++)
vector.get(i);
}
};
};
thread1.start();
thread2.start();
while(Thread.activeCount()>10) {
}
}
}
}
3. ConcurrentModificationException异常
在对Vector等容器并发地进行迭代修改时,会报ConcurrentModificationException异常,但是在并发容器中不会出现这个问题。关于这个异常请参考http://www.cnblogs.com/dolphin0520/p/3933551.html
出处:http://www.cnblogs.com/dolphin0520/
Java并发(5):同步容器的更多相关文章
- java并发:同步容器&并发容器
第一节 同步容器.并发容器 1.简述同步容器与并发容器 在Java并发编程中,经常听到同步容器.并发容器之说,那什么是同步容器与并发容器呢?同步容器可以简单地理解为通过synchronized来实现同 ...
- Java并发编程--同步容器
BlockingQueue 阻塞队列 对于阻塞队列,如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤 ...
- Java 并发 线程同步
Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大 ...
- java多线程总结-同步容器与并发容器的对比与介绍
1 容器集简单介绍 java.util包下面的容器集主要有两种,一种是Collection接口下面的List和Set,一种是Map, 大致结构如下: Collection List LinkedLis ...
- Java并发编程:CopyOnWrite容器的实现
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- Java线程安全同步容器
线程安全同步容器(使用 synchronized关键字) 1.ArrayList->Vector,Stack 2.HashMap->HashTable(key.value不能为null) ...
- Java并发队列与容器
[前言:无论是大数据从业人员还是Java从业人员,掌握Java高并发和多线程是必备技能之一.本文主要阐述Java并发包下的阻塞队列和并发容器,其实研读过大数据相关技术如Spark.Storm等源码的, ...
- Java并发与同步
Java中并发的形式无非是多线程和多进程两种形式.这两种形式都是能够利用多核来发挥计算能力的. 先说并发: 多进程意味着同一时候执行多个JVM.这个代价通常比多线程高,每一个JVM都有自己的堆栈.都要 ...
- Java并发框架——同步状态的管理
整个AQS框架核心功能都是围绕着其32位整型属性state进行,一般可以说它表示锁的数量,对同步状态的控制可以实现不同的同步工具,例如闭锁.信号量.栅栏等等.为了保证可见性此变量被声明为volatil ...
- Java并发——线程同步Volatile与Synchronized详解
0. 前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52370068 面试时很可能遇到这样一个问题:使用volatile修饰in ...
随机推荐
- [转]Mac系统安装软件提示软件已损坏的解决办法
很多朋友下载本站提供的Mac软件资源,安装提示“xxx软件已损坏,打不开,您应该将它移到废纸篓”的提示,其实并不是软件本身有问题,而是Mac系统的一个安全机制问题,按照如下方法操作,即可打开并安装本站 ...
- 在grub的rescue模式修复linux引导
今天在windows 10系统收到系统更新通知,没看清楚就手贱点了马上更新.以为只是像那些普通更新一样重启一下更新就完了,万万没想到这个是覆盖更新,也就是说这是一个全新的系统更新而不是系统补丁.在安装 ...
- CH Round #54 - Streaming #5 (NOIP模拟赛Day1)(被虐瞎)
http://ch.ezoj.tk/contest/CH%20Round%20%2354%20-%20Streaming%20%235%20%28NOIP%E6%A8%A1%E6%8B%9F%E8%B ...
- Python+selenium之读取配置文件内容
Python+selenium之读取配置文件内容 Python支持很多配置文件的读写,此例子中介绍一种配置文件的读取数据,叫ini文件,python中有一个类ConfigParser支持读ini文件. ...
- AWS那些需要注意的问题
自己走过的坑,才知道 1.1 EC2实例限制 EC2实例在申请超过20台后,会有数量限制. 解决方法: AWS控制台需要提工单,进行申请解除限制.把EC2数量提高到50台或者100台 此工单审核大概要 ...
- Hibernate中持久化类与持久化对象
1.JavaBean类 JavaBean类是实体类,必须一下属性,private修饰的成员属性,public修饰的getter与setter访问方法,public修饰的空参构造器,实现Serializ ...
- android应用安全——代码安全(android代码混淆)
android2.3的SDK开始在eclipse中支持代码混淆功能(理论上java都支持混淆,但关键在于如何编写proguard的混淆脚本,2.3的SDK使用简单的配置就可以实现混淆).使用SDK2. ...
- BF算法 + KMP算法
准备: 字符串比大小:比的就是字符串里每个字符的ASCII码的大小.(其实这样的比较没有多大的意义,我们关心的是字符串是否相等,即匹配等) 字符串的存储结构:同线性表(顺序存储+链式存储) 顺序存储结 ...
- [LintCode] 二叉树的中序遍历
The recursive solution is trivial and I omit it here. Iterative Solution using Stack (O(n) time and ...
- css3动画效果:3 3D动画
立方体旋转动画效果 css #container{ width: 400px; height: 400px; ; ; -webkit-perspective-origin:50% 225px; per ...