JUC解决集合不安全问题
list不安全
ArrayList 在并发情况下是不安全的
可能会导致java.util.ConcurrentModificationException并发修改异常!
示例 (需要多运行几次,有时候可能不会出现异常)
public class ListOnSafe {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}

解决方案一:使用安全的集合Vector
集合Vector为什么安全?通过源码我们就可以知道。
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
修改后的示例如下
public class ListOnSafe {
public static void main(String[] args) {
List<String> list = new Vector<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
修改后结果显示正常,当然我们也可以手动给list操作时加锁,保证结果正确,原理和Vector一样。
解决方案二:使用Collections集合工具类类
Collections.synchronizedList(new ArrayList<>());
修改后的案例如下:
public class ListOnSafe {
public static void main(String[] args) {
List<String> list= Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 100; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
解决方案三:使用CopyOnWriteArrayList<>() (推荐)
CopyOnWriteArrayList<>()是属于JUC编程内容,为什么推荐使用CopyOnWriteArrayList,而不使用上面两种方式呢,原因是上面两种方式本质都是通过加synchronize实现集合安全操作。加synchronize后会影响性能。lock的效率更高。
CopyOnWriteArrayList:多个线程操作同一份资源。如果其中一线程修改资源内容,系统会复制一个副本给该线程操作,完成后把新的副本资源的引用地址赋值给了之前那个旧的的资源地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。(所有可变操作都是通过对底层数组进行一次新的复制来实现。)
CopyOnWriteArrayList 合适读多写少的场景,不过这类慎用 ,因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。
CopyOnWriteArrayList的一些源码
private transient volatile Object[] array;
示例
public class ListOnSafe {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 100; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
参考文章:https://blog.csdn.net/weixin_42146366/article/details/88016527
set不安全
set和list一样,在并发情况下可能会找出线程不安全。
一般我们有两种方法可以解决并发情况下set不安全的情况。
- 使用Collections工具类中安全的set
- 使用JUC编程CopyOnWriteArraySet 写入时复制方案
示例
public class SetOnSafe {
public static void main(String[] args) {
//不安全
//Set<String> set = new HashSet<>();
//Collections 方式 安全
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
//JUC下的写入时复制 安全
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
Map不安全
下面再来了解一下Map在并发情况下的不安全问题。
同样有两种解决方法
- Collections工具类中安全的map集合操作。
- JUC中的安全的map集合操作。
示例
public class MapUnSafe {
public static void main(String[] args) {
//不安全
Map<String,String> map=new HashMap<>();
//collections 安全
//Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
//juc 安全
//ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}

浙公网安备 33010602011771号