集合类的一些并发操作问题
2020-04-23 10:05
851 查看
线程不安全的集合类:
ArrayList()
LinkedList()
HashSet()
HashMap()
TreeSet()
TreeMap()
并发操作集合类:
ArrayList
示例代码:
/** * 集合类不安全的问题 * ArrayList */ public class ContainerNotSafeDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); for (int i = 0; i < 100; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString()); System.out.println(list); }, String.valueOf(i + 1)).start(); } } }
100个线程同时向同一个
List中添加一个元素,系统可能会报出
java.util.ConcurrentModificationException这个并发修改集合的错误。
解决方案:
- 使用
new Vector<>()
类,不建议,因为底层使用了synchronized
,性能低 - 使用
Collections.synchronizedList(new ArrayList<>())
,是Collections
工具类提供的方法 - 使用
new CopyOnWriteArrayList<>()
,推荐使用,采用写时复制的方式,解决并发问题,在写的时候不影响其他线程对集合的读操作。
new Vector<>()
源码:
/** * Appends the specified element to the end of this Vector. * * @param e element to be appended to this Vector * @return {@code true} (as specified by {@link Collection#add}) * @since 1.2 */ public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; }
这个类的底层大量使用了
synchronized关键字。
new CopyOnWriteArrayList<>()
源码:
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { // 可重入锁 final ReentrantLock lock = this.lock; // 加锁 lock.lock(); try { // 获取集合 Object[] elements = getArray(); // 获取集合的长度 int len = elements.length; // 复制一个数组并将长度加1 Object[] newElements = Arrays.copyOf(elements, len + 1); // 将元素添加到新数组的最后 newElements[len] = e; // 将数组的引用指向新数组 setArray(newElements); return true; } finally { // 最后解锁 lock.unlock(); } } /** * Gets the array. Non-private so as to also be accessible * from CopyOnWriteArraySet class. */ final Object[] getArray() { // 获取数组 return array; } /** The array, accessed only via getArray/setArray. */ // 使用volatile 定义的数组 private transient volatile Object[] array; /** * Sets the array. */ final void setArray(Object[] a) { // 将array的引用指向a数组 array = a; }
CopyOnWrite容器即写时复制容器
源码中
add的执行过程:
往一个容器添加元素的时候,不直接往当前容器
Object[]添加,而是先将容器
Object[]进行Copy,复制出一个新的容器
Object[] newElements,然后往新的容器
Object[] newElement中添加元素,添加完元素之后,再将原容器的引用指向新的容器
setArray(newElement)。
好处:
可以对
CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以
CopyOnWrite容器也是一种读写分离的思想,读和写在不同的容器。
HashSet
示例代码:
/** * 集合类不安全的问题 * ArrayList */ public class ContainerNotSafeDemo { public static void main(String[] args) { Set<String> set = new HashSet<>(); for (int i = 0; i < 100; i++) { new Thread(() -> { set.add(UUID.randomUUID().toString()); System.out.println(set); }, String.valueOf(i + 1)).start(); } } }
与
ArrayList相同,100个线程同时修改一个
Set集合,也出现了
java.util.ConcurrentModificationException错误。
解决方案:
- 使用
Collections.synchronizedSet(new HashSet<>())
,是Collections
工具类提供的方法。 - 使用
new CopyOnWriteArraySet<>()
,其底层的实现是使用new CopyOnWriteArrayList<>()
。
HashSet
源码:
/** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<>(); }
Hash的底层使用的是
HashMap。
// Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); /** * Adds the specified element to this set if it is not already present. * More formally, adds the specified element <tt>e</tt> to this set if * this set contains no element <tt>e2</tt> such that * <tt>(e==null ? e2==null : e.equals(e2))</tt>. * If this set already contains the element, the call leaves the set * unchanged and returns <tt>false</tt>. * * @param e element to be added to this set * @return <tt>true</tt> if this set did not already contain the specified * element */ public boolean add(E e) { return map.put(e, PRESENT)==null; }
HashSet的元素是
HashMap的
key,
HashMap的
value是一个
Object类型的常量
PRESENT。
CopyOnWriteArraySet
源码
/** * Creates an empty set. */ public CopyOnWriteArraySet() { al = new CopyOnWriteArrayList<E>(); }
CopyOnWriteArraySet的底层使用的是
CopyOnWriteArrayList。
HashMap
示例代码:
/** * 集合类不安全的问题 * HashMap */ public class ContainerNotSafeDemo { public static void main(String[] args) { Map<String, String> map = new HashMap<>(); for (int i = 0; i < 100; i++) { new Thread(() -> { map.put(Thread.currentThread().getName(), UUID.randomUUID().toString()); System.out.println(map); }, String.valueOf(i + 1)).start(); } } }
运行代码也会抛出相同的错误
java.util.ConcurrentModificationException。
解决方案:
- 使用工具类
Collections
提供的方法synchronizedMap(new HashMap<>())
。 - 使用
new ConcurrentHashMap<>()
,ConcurrentHashMap
底层使用的是分段锁,相对于锁整个map
,分段锁具有更高的并发性。
- 点赞
- 收藏
- 分享
- 文章举报
相关文章推荐
- 并发操作的一致性问题
- c++类的一点操作 (运算符重载求两个点的一些问题)完成这些算是c++入门了
- git初始化操作以及一些问题的解决
- 数据库中的并发操作带来的一系列问题
- 《Java并发编程的艺术》读书笔记——并发中的一些基础知识和问题
- 数据库并发操作的一致性问题
- 数据库中的并发操作带来的一系列问题及解决方法
- 黄聪:第2章 并发操作的一致性问题 (2)
- 《并发操作一致性问题》已全部完成
- java中对文件的一些操作(删除,复制,计算大小..)和一些数学问题代码详解
- Git远程库操作遇到的一些问题
- 并发操作的一致性问题
- Iphone数组一些基础操作 NSArray/NSMutableArray,以及内存注意问题。
- javascript中table操作的一些问题【insertCell(),Y.Node.create()】
- ORACLE空间管理实验6:块管理之ASSM下插入操作--高水位的影响及大并发插入的性能问题
- 域控制器之操作主机中应注意的一些常见问题 推荐
- Git远程库操作遇到的一些问题
- PHP添加文件锁避免高并发操作同一个文件篡改数据问题
- 【Python数据分析】Python3操作Excel(二) 一些问题的解决与优化
- wince EDB操作的一些问题