主要在add,remove时报: java.util.ConcurrentModificationException(并发修改异常)
为什么会出现这个异常:
比如当A线程在添加数据的时候,B线程突然进来,把A线程要添加数据的位置,增加了自己的数据,紧接着A线程重新执行,再次修改了刚刚的数据,把B线程的数据,修改成自己的数据,这个会导致数组内有空值或者抛出异常
解决办法:
- 使用Vector
- 使用Collections
- 使用CopyandWriteArraryList();
在写时加锁,先将整个数据对象复制一份,并将长度增加1,然后把数据添加到数组的最后位置,返回true
补充:synchronized里面的lock
为什么这里使用这个对象作为锁(对象锁):
首先就是CopyandWriteArraryList可以在多个类中被new,那么就意味着可以对多个对象CopyandWriteArraryList进行addandRemove,那么如果使用带static的锁,意味着所有的copyandWriteArraryList对象被锁住了,虽说不会出错,但出现多个对象使用同一把锁,所以使用对象锁更合适,一个对象使用一个锁。
Set解决线程安全问题
原因:可以查看相关资料
HashSet是线程不安全的,底层实现HashMap,其中key作为值,Value是一个Object常量
解决办法:
- 使用Collections
- 使用CopyandWriteArrarySet()(底层实现CopyandWriteArraryList)
Map解决线程不安全问题
HashMap是线程不安全
原因:可以查看相关资料
解决办法:
- Collections
- ConcurrentHashMap
ConcurrentHashMap:
JDK1.7之前,使用分段锁
分段锁:
Hashtable之所以效率低下主要是因为其实现使用了synchronized关键字对put等操作进行加锁,而synchronized关键字加锁是对整个对象进行加锁,也就是说在进行put等修改Hash表的操作时,锁住了整个Hash表,从而使得其表现的效率低下;因此,在JDK1.5~1.7版本,Java使用了分段锁机制实现ConcurrentHashMap.
简而言之,ConcurrentHashMap在对象中保存了一个Segment数组,即将整个Hash表划分为多个分段;而每个Segment元素,即每个分段则类似于一个Hashtable;这样,在执行put操作时首先根据hash算法定位到元素属于哪个Segment,然后对该Segment加锁即可。因此,ConcurrentHashMap在多线程并发编程中可是实现多线程put操作。
缺点:并发度受Segment的个数限制
JDK1.8
使用CAS+Synchronized保证数据安全(可以参考具体资料)