单线程环境下使用ArrayList
List<String> list =new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
for (String element:list){
System.out.println(element);
}
我们经常在项目中会这样使用ArrayList,我们来分析一下,当我们执行底层干了些什么
List<String> list =new ArrayList<>();
底层创建了一个空的数组,初始化值为10,当我们执行add方法的时候,如果超过10.那么就会扩容,扩容值的大小为原值的一半,也就是5个,使用下列方法进行扩容
elementData = Arrays.copyOf(elementData, newCapacity);
但是ArrayList在单线程下是线程不安全的
多线程下使用ArrayList
List<String> list1 =new ArrayList<>();
for (int i=0;i<50;i++){
new Thread(()->{
list1.add(UUID.randomUUID().toString().substring(0,8));
System.out.println("线程名称= "+Thread.currentThread().getName()+"线程id "+Thread.currentThread().getId()+"数组list ="+list1);
},String.valueOf(i)).start();
}
截图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HJmdgD3z-1603943188867)(https://raw.githubusercontent.com/xiaofeifei321/Picture/master/img20201029111328.png)]
分析:
多线程我们观看源码可以知道,在进行写操作的时候,方法上为了保证并发性,并没有添加synchronise修饰,所以并发写的去操作List的时候,就会出现 java.util.ConcurrentModificationException 异常(这里注意一点我们不输出 System.out.println(list1)程序并不会报错,从这个现象看,并发读写时候才会出现问题,并发写的时候没出现问题)
![1](https://raw.githubusercontent.com/xiaofeifei321/Picture/master/img20201029111242.png)
解决方法
Vector
我们通过阅读源码知道,ArrayList在添加元素的add方法上没有加synchronized锁,但是Vector类上是添加了同步锁的
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
这样每一个线程操作,就不会出现线程不安全的问题,但是因为加锁,不会出现线程安全问题,但是会导致并发度降低
采用Collection集和工具类
List<String> list4= Collections.synchronizedList(new ArrayList<>());
采用集和工具类,在ArrayList外面包装一层同步机制
采用JUC里面的方法
CopyOnWriteArrayList copyOnWriteArrayList=new CopyOnWriteArrayList();
copyOnWriteArrayList.add(1);
copyOnWriteArrayList.add(2);
copyOnWriteArrayList.add(3);
copyOnWriteArrayList.add(4);
System.out.println(copyOnWriteArrayList);
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;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
final void setArray(Object[] a) {
array = a;
}
CopyOnWriteArrayList是这样干的,当我们往一个容器中添加元素的时候,不直接往当前容器Object[]添加,而是先将Object[]进行copy,复制出一个新的容器,然后在新的容器中进行添加元素的操作,万事之后,将原来容器的引用指向新的容器,这样做的好处是可以提高并发度,当前的容器并不需要加锁。
说明参考
文章为看视频博客学习过程中总结,方便自己以后好复习
https://www.bilibili.com/video/BV18b411M7xz
http://moguit.cn/#/