线程安全 -- 同步容器
同步容器
同步容器分两类,一种是Java提供好的类,另一类是Collections类中的相关同步方法。
(1)ArrayList的线程安全类:Vector,Stack
Vector实现了List接口,Vector实际上就是一个数组,和ArrayList非常的类似,但是内部的方法都是使用synchronized修饰过的方法。
Stack它的方法也是使用synchronized修饰了,继承了Vector,实际上就是栈
使用举例(Vector):
实例方法变一下, 其他和arrylist相同
private static List<Integer> list = new Vector<>();
输出结果:
size = 5000. 线程安全
但是同步容器不一定所有时候都是线程安全的**
所以Vector也不是完全的线程安全的,比如:
错误[1]:删除与获取并发操作
运行后
为什么用synchronize修饰后的方法任有线程安全问题呢?
原因分析:同时发生获取与删除的操作。当两个线程在同一时间都判断了vector的size,假设都判断为9,而下一刻线程1执行了remove操作,随后线程2才去get,所以就出现了错误。synchronized关键字可以保证同一时间只有一个线程执行该方法,但是多个线程同时分别执行remove、add、get操作的时候就无法控制了。**
(2)HashMap的线程安全类:HashTable
把之前HashMap例子中的实例方法换一下即可, 其他都一样
private static Map<Integer, Integer> map = new Hashtable<>();
输出结果:
size = 5000. 线程安全
-
保证安全性:使用了synchronized修饰
-
不允许空值(在代码中特殊做了判断)
-
HashMap和HashTable都使用哈希表来存储键值对。在数据结构上是基本相同的,都创建了一个继承自Map.Entry的私有的内部类Entry,每一个Entry对象表示存储在哈希表中的一个键值对。
3)Collections类中的相关同步方法
Collections类中提供了一系列的线程安全方法(如下)用于处理ArrayList等线程不安全的Collection类
使用方法:
//实例
private static List<Integer> list = Collections.synchronizedList(Lists.newArrayList());
//多线程调用方法
private static void update(int i) {
list.add(i);
}
Collections类源码分析:
内部操作的方法使用了synchronized修饰符
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
...
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
...
}