5_集合的线程安全
Posted root_zhb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5_集合的线程安全相关的知识,希望对你有一定的参考价值。
集合的线程安全
1、ArrayList
1.1、线程不安全演示
public class NotSafeDemo {
//多个线程同时对集合进行修改
public static void main(String[] args) {
List list = new ArrayList();
for (int i = 0; i < 100; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, "线程" + i).start();
}
}
}
报错:java.util.ConcurrentModificationException
1.2、解决方案-Vector
List list = new Vector();
1.3、解决方案-Collections
List list= Collections.synchronizedList(new ArrayList());
1.4、解决方案-CopyOnWriteArrayList
写时复制技术
List list=new CopyOnWriteArrayList<>();
-
特性:
- 它最适合于具有以下特征的应用程序:List 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。
- 它是线程安全的。
- 因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。
- 迭代器支持 hasNext(), next()等不可变操作,但不支持可变 remove()等操作。
- 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
-
总结:
-
独占锁效率低:采用读写分离思想解决
-
写线程获取到锁,其他写线程阻塞
-
复制思想
当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行 Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这时候会抛出来一个新的问题,也就是数据不一致的问题。如果写线程还没来得及写会内存,其他的线程就会读到了脏数据。
-
-
“动态数组”机制
- 它内部有个“volatile 数组”(array)来保持数据。在“添加/修改/删除”数据时,都会新建一个数组,并将更新后的数据拷贝到新建的数组中,最后再将该数组赋值给“volatile 数组”, 这就是它叫做 CopyOnWriteArrayList 的原因
- 由于它在“添加/修改/删除”数据时,都会新建数组,所以涉及到修改数据的操作,CopyOnWriteArrayList 效率很低;但是单单只是进行遍历查找的话,效率比较高。
-
“线程安全”机制
- 通过 volatile 和互斥锁来实现的。
- 通过“volatile 数组”来保存数据的。一个线程读取 volatile 数组时,总能看到其它线程对该 volatile 变量最后的写入;就这样,通过 volatile 提供了“读取到的数据总是最新的”这个机制的保证。
- 通过互斥锁来保护数据。在“添加/修改/删除”数据时,会先“获取互斥锁”,再修改完毕之后,先将数据更新到“volatile 数组”中,然后再“释放互斥锁”,就达到了保护数据的目的。
2、HashSet
2.1、线程不安全演示
public class NotSafeDemo {
//多个线程同时对集合进行修改
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().substring(0,8));
System.out.println(set);
}).start();
}
}
}
2.2、解决方案CopyOnWriteArraySet
Set<String> set=new CopyOnWriteArraySet<>();
3、HashMap
3.1、线程不安全演示
public class NotSafeDemo {
//多个线程同时对集合进行修改
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
for(int i=0;i<100;i++){
String key=String.valueOf(i);
new Thread(()->{
map.put(key,UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
}).start();
}
}
}
3.2、解决方案ConcurrentHashMap
Map<String,String> map=new ConcurrentHashMap<>();
以上是关于5_集合的线程安全的主要内容,如果未能解决你的问题,请参考以下文章