标签:容器 ArrayList list 安全 static 线程 new
new ArrayList () 会创建一个容量为10的Object数组,ArrayList每次扩容是原来的一半(oldCapacity + (oldCapacity >> 1)),ArrayList是线程不安全的。
ArrayList线程不安全的代码
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* ArrayList线程不安全的代码
*/
public class ArrayListNoSafe {
public static void main(String[] args) {
listNotSafe();
}
private static void listNotSafe() {
List<String> list = new ArrayList<>();
for (int i = 0; i < 40; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
//运行出现 ConcurrentModificationException 异常
}
从多个维度进行分析
1) 故障现象
java.util.ConcurrentModifitionException (并发修改异常)
2) 导致原因
多个线程并发同时争取同一份资源,从而导致并发修改异常
3) 解决办法
3-1) 解决办法1,使用 Vector
private static void listNotSafe() {
List<String> list = new Vector<>();
for (int i = 0; i < 40; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
问题: 已经存在了 Vector ,那为什么还要用 ArrayList ?
答: Vector 是线程安全的,也没有出现 并发修改异常(ConcurrentModifictionException),然而代价是加了 Synchronized (锁),数据一致性提高了,但是并发性降低了,所以ArrayList 和 Vector 效率提高了。
3-2)解决办法2,使用 Collections.synchronizedList(new ArrayList());
private static void listNotSafe() {
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 40; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
通过 Collections的 synchronizedMap , synchronizedSet 反向可以推断出 HashMap 和 HashSet 是线程不安全的
3-3) 解决办法3,使用 CopyOnWriteArrayList --写时复制技术
private static void listNotSafe() {
List<String> list = new CopyOnWriteArrayList();
for (int i = 0; i < 40; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
CopyOnWriteArrayList 类写时复制原理
final transient ReentrantLock lock = new ReentrantLock();
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();
}
}
copyOnWrtie 容器即写时复制的容器,往容器中添加元素的时候,不直接往当前容器 object[] 添加,而是先将当前容器 object [] 进行copy复制出一个新的容器 object [] newElements;
然后向 newElements 中添加元素,添加完元素后,再将原容器的引用指向新容器 setArray(new Elements)
这样做的好处是可以对 copyonwrite 容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素,所以 copyonwrite容器也是一种读写分离的思想,读和写不同的容器。
4),优化建议
在高并发的环境下建议使用 copyonWriteArrayList来使线程安全
5),扩展
HashSet,HashMap也是线程不安全的,是否会出现ArrayList的问题呢?
public static void main(String[] args) {
setNotSafe();
}
private static void setNotSafe() {
Set<String> set = new HashSet<>();
for (int i = 0; i < 40; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
也会出现 java.util.ConcurrentModifiction 并发修改异常 ,HashMap同理
HashSet底层是HashMap key为存入的值,而value 存储的是常量 private static final Object PRESENT = new Object();
HashSet 解决办法 可以使用 CopyOnWriteArraySet
同理 HashMap也是线程不安全的 解决办法是使用 ConcurrentHashMap 来解决
标签:容器,ArrayList,list,安全,static,线程,new 来源: https://blog.csdn.net/Maxiao1204/article/details/120424389
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。