list
不安全类是什么?
不安全类是指在多线程并发的时候不能保证数据正确性的类,通常是由于这些类并没有加锁造成的。
为什么不设计成加锁的?
其实,在list之前有个集合类vector,它是内部加锁,它是一个线程安全类。不优先使用它的原因是加锁可以保证数据的正确性,但却降低了并发效率。list单线程安全,多线程不安全。并发条件下会产生ConcurrentModificationException异常(并发修改异常)
如何做到保证数据的正确性呢?
- vector替代list(并发效率低)
- 用Collections.synchronizedList(list)包装list(有synchronized修饰的方法效率低)
- 使用juc里的CopyOnWriteArrayList替代list(推荐使用)写入时复制,读写分离的思想。
CopyOnWriteArrayList 写时复制,读写分离
既能保证数据的正确性,又不会使并发效率变低。它的add源码:
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
复制一个原来数组副本,在副本里“写入元素”,最后再用写完的副本替换原本的数组,即我们在
“写”时可以“读”,这两个操作所使用的不是一个数组,不会产生影响。
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
这样写时复制实现了读写分离,我们不需要在读的时候加锁(之前读需要加锁是因为读写不能同时进行,但一旦给读加了锁,那么读也不能同时进行,就降低了并发效率)
但是,我们每“写入”一个元素就要复制扩容一次数组,是非常耗时耗资源的,所以当我们需要写入较多数据的时候,CopyOnArrayList就不那么合适了。
Set
也是不安全的集合类
将不安全的集合变成安全集合的方法:
1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
2.Set<String> set = new CopyOnWriteArrayListSet<>();
HashSet底层是什么?
//默认的空参初始化方法
public HashSet() {
map = new HashMap<>();
}
//使用HashSet的add方法,依然是调用HashMap的底层put方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
也就是说HashSet的底层就是HashMap
Map
也是不安全的集合类
变成安全的方法:
1.Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
无论读取还是写入,都会进行加锁,当并发级别特别高,线程之间在任何操作上都会进行等待,效率低。
2.Map<String,String> map = new ConcurrentHashMap<>();
采用分段锁技术,其中Segment继承于ReentrantLock。不会像HashTable(线程安全) 那样不管是put还是get操作都需要做同步处理,理论上ConcurrentHashMap支持CurrentLevel(Segment数组数量)的线程并发。每当一个线程占用锁访问一个Segment时,不会影响其他的Segment.
节选自狂神说JUC学习笔记+补充(一)是其中的一部分,为方便阅读将其抽取出来,查看完整笔记见
演绎法5919:狂神说JUC学习笔记+补充(一)zhuanlan.zhihu.com