写文章

Java并发编程之支持并发的list集合你知道吗

2020-03-28 09:59:18

85 | 0 | 0

Java并发编程之-list集合的并发.

我们都知道Java集合类中的arrayList是线程不安全的。那么怎么证明是线程不安全的呢?怎么解决在并发环境下使用安全的list集合类呢?

本篇是《凯哥(凯哥Java:kagejava)并发编程学习》系列之《并发集合系列》教程的第一篇:

本文主要内容:怎么证明arrayList不是线程安全的?怎么解决这个问题?以及遇到问题解决的四个步骤及从源码来分析作者思路。

一:怎么证明arrayList在并发情况下是线程不安全的呢?

创建一个list,用多个线程向list中添加数据。来看看结果


查看运行结果:


我们发现了一个异常:java.util.ConcurrentModificationException

java.util.ConcurrentModificationException是什么

这个异常什么意思呢?我们来看看这个异常源码中类的注释信息:


This exception may be thrown by methods that have detected concurrent(此异常可能由检测到并发的方法引发).

一般可以理解为,这是并发导致的异常。那么在并发情况下出现了异常。是不是从侧面说明arrayList是不安全的呢?

二:怎么解决这个问题

这里凯哥顺便说下,解决问题的一般步骤。

1:怎么操作导致的故障及现象是什么?

操作:多个线程对list进行add添加操作的时候

结果:抛出了java.util.ConcurrentModificationException异常信息

2:分析产生这个问题的原因

举个现实生活中的例子。签到表,这个大家都见过吧,应该都签到过吧。比如现在有个会议很多人来参与,需要签到。现在,司小司正在签到表上写自己的名字时候,小明非要看签到表上面有没有自己名字。因为司小司正在签到进行中,小明硬是要查看,把签到表抢过去,结果就是签到表被撕坏了或者是司小司的笔在签到表上留下了长长的痕迹。如果上面这个例子用计算机角度分析的话。

两个线程(司小司和小明)对一个共享变量(签到表,可以理解为是人名的集合)进行读写操作(司小司签到是写操作,小明要查看自己是否签到了,可以理解为读操作),因为两个线程都来竞争共享资源。后果就是签到表被撕坏了或者是司小司的笔在签到表上留下了长长的痕迹。异常现象。用到上面我们多个线程对list进行操作的时候,就抛异常了多线程并发修改异常信息。

3:解决方案是什么?

1:使用线程安全的List的子类Vectory

List list = new Vectory();

查看vectory的add方法源码:


发现,原来vector的add方法是加的并发锁来保证线程安全的

2:使用collections工具类的sync方法

List list = Colletcions.synchronizedList(new ArrayList<>());

查看源码:



原来都是synchronized的。

我们在来看看synchronizedList方法上面的注释。


发现,原来源码中是把整个list对象作为同步锁的锁。这样来保证线程安全的

4:解决方案可以优化吗?优化的建议是什么?

我们知道synchronized关键字是同步锁机制。强制并行转化成串行的一种方案。这种对性能消耗比较大。有没有更其他可以优化的方案吗?

来看看使用JUC并发包下的:CopyOnWriteArrayList(写时复制list)来解决吧。

先来看看这个类的add方法的源码:


从源码中,我们可以看到复制了一个新的list集合,将新元素在新集合中操作。那么为什么这种操作就不会出现并发异常呢?

因为这种思想,可以理解为读写分离的思想。因为get还是使用原来list的get的方法。写的时候,在复制一份原来的,然后再复制出来的基础上进行修改的。那么怎么保证数据问题呢?我们从源码中可以看到使用到了ReentrantLock(关于锁相关的。凯哥(凯哥Java:kaigejava)将在后面详细的讲解的)锁来控制的。

那么现在使用CopyOnWriteArrayList来模拟下文章开头签到例子。

司小司再签到的时候,先把签到表复制一份,然后再新的复制出来的签到表中进行签到。小明是原来签到表查看自己的信息的。这样就不会出现争强情况了。



0

收藏
分享