Java多线程并发--使用CopyOnWrite实现并发写操作
2021-06-24 本文已影响0人
砌月东谷
1.1 JAVA的同步容器类
如Hashtable,ArrayList,Vector和Stack等都是并发容器类,其中Stack继承于Vector,同步容器类是一种串行化而且线程安全的容器,但是在早期设计的时候没有考虑一些并发读写的问题,当对同步容器类进行并发读写的时候会出现ConcurrentModificationException异常
ArrayList<String> list=new ArrayList<>();
list.add("苹果");
list.add("香蕉");
Iterator<String> iter=list.iterator();
while(iter.hasNext()){
System.out.printIn(iter.next());
list.add("西瓜");
}
此时会出现ConcurrentModificationException异常,原因主要是:
- ArrayList等同步容器类有一个全局变量modeCount(该属性是从父类AbstractList继承而来),而且其内部类ITR中有一个expectedModCount变量,进行迭代的时候(如iter.next()),迭代器会确保modCount和expectedModCount的值相等,如果不相等就会报ConcurrentModificationException异常,在迭代的同时,又进行了写操作,modCount++,所以modCount!=expectedModCount
解决这个异常的方法是使用并发容器类
1.2 JAVA并发容器类
JUC提供了多种并发类容器,在改善性能的同时也解决了上述的异常,因此,当我们在多线程环境下变成,建议使用并发类容器来替代创痛的同步类容器
同步类容器 | 并发类容器 |
---|---|
HashTable | ConcurrentHashMap |
Vector | CopyOnWriteArrayList |
除了上述两个并发类容器外,JUC还提供了CopyWriteArraySet、ConcurrentLinkedQueue、PriorityBlockingQueue,把上面的代码ArrayList改为CopyOnWriteArrayList,其他代码不变就不会报错
CopyOnWrite容器包含CopyOnWriteArrayList和CopyOnWriteArraySet,其实现并发读写的时候会经历两个过程
- 先将当前容器复制一份,然后向新的容器(复制后的容器)里添加元素,并不会直接向原来的容器添加元素
- 当添加完元素后,再将引用指向新的容器,原容器等待回收
这样一来就利用冗余实现了容器的读写分离,当对容器写操作的同时,容器此时已经复制了一份,但引用还没有改变指向的时候,原容器仍然可以处理用户的读请求,从而实现了没有锁的情况下的并发读写,即在原容器中处理读请求,在新容器中处理写请求
对于“读多写少”的业务,适合使用CopyOnWrite容器,相反则不适合,因为复制容器存在性能的消耗