5、集合类不安全
List不安全
单线程下使用List都是安全的
import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ListTest { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3); list.forEach(System.out::println); List<String> list2 = new ArrayList<>(); for (int i = 0; i < 10; i++) { list2.add(String.valueOf(i)); } list2.forEach(System.out::println); } }
多线程下使用List
首先我们要知道,在高并发的环境下List容器是不安全的
比如:下面的例子,会报错 java.util.ConcurrentModificationException
并发修改异常
为什么会报这个异常呢?
import java.util.ArrayList; import java.util.List; import java.util.UUID; // java.util.ConcurrentModificationException 并发修改异常 public class ListTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0, 5)); System.out.println(list); }, String.valueOf(i)).start(); } // System.out.println(list.size()); //main线程,可能在没有数据的情况下,先输出 } }
解决方案
有三种:
1、找一个替代的容器:
new Vector<>(); 是线程安全的容器
2、使用工具类:
Collections.synchronizedList(new ArrayList<>()); Collection是容器的父类
3、使用JUC提供的线程安全的容器:
new CopyOnWriteArrayList<>(); // 拷贝一份原来的数组,在插入数据
使用Vector容器
import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.Vector; // java.util.ConcurrentModificationException 并发修改异常 public class ListTest { public static void main(String[] args) { /** * 解决方法: * 1、new Vector<>(); 是线程安全的容器 */ List<String> list = new Vector<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0, 5)); System.out.println(list); }, String.valueOf(i)).start(); } // System.out.println(list.size()); //main线程,可能在没有数据的情况下,先输出 } }
Vector底层加了synchronized
使用Collections工具类
package com.zxh.unsafe; import java.util.*; // java.util.ConcurrentModificationException 并发修改异常 public class ListTest { public static void main(String[] args) { /** * 解决方法: * 1、new Vector<>(); 是线程安全的容器 * 2、Collection是容器的父类:使用工具类 Collections.synchronizedList(new ArrayList<>()); */ List<String> list = Collections.synchronizedList(new ArrayList<>()); // 创建同步容器 for (int i = 0; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0, 5)); System.out.println(list); }, String.valueOf(i)).start(); } // System.out.println(list.size()); //main线程,可能在没有数据的情况下,先输出 } }
JUC提供的线程安全的容器
package com.zxh.unsafe; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; // java.util.ConcurrentModificationException 并发修改异常 public class ListTest { public static void main(String[] args) { /** * 解决方法: * 1、new Vector<>(); 是线程安全的容器 //同步方法 * 2、Collection是容器的父类:使用工具类 Collections.synchronizedList(new ArrayList<>()); 同步方法 * 3、使用JUC提供的线程安全的容器 new CopyOnWriteArrayList<>(); // 拷贝一份原来的数组,在插入数据 */ List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0, 5)); System.out.println(list); }, String.valueOf(i)).start(); } // System.out.println(list.size()); //main线程,可能在没有数据的情况下,先输出 } }
CopyOnWriteArrayList 底层实现
进入查看源码
-
首先查看一下容器是如何构造
1、创建数组,调用setArray()方法
3、这里涉及到了volatile
关键字,这在下面会讲
-
看一下是如何添加的
1、查看add方法
2、进入该类是实现的方法
3、实现原理,使用了Lock锁,同步操作,将原来的数组复制一份,再添加数据。
Set不安全
Set容器和List一样,不同的是:
1、无序的
2、不能重复
为什么是无序、不能重复呢?接下来说明。
单线程和多线程下操作Set
单线程下操作Set
import java.util.HashSet; import java.util.Set; import java.util.UUID; public class SetTest { public static void main(String[] args) { Set<String> set = new HashSet<>(); for (int i = 0; i < 10; i++) { set.add(UUID.randomUUID().toString().substring(0, 5)); System.out.println(set); } } }
多线程下操作Set
-
和List相同都会存在并发问题
import java.util.HashSet; import java.util.Set; import java.util.UUID; /** * 同理可证:java.util.currentModifyException */ public class SetTest { public static void main(String[] args) { Set<String> set = new HashSet<>(); 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(); } } }
解决方法
有两种:
因为Set没有替代的容器。
-
使用工具类
-
Collections.synchronizedSet(new HashSet<>());
-
-
使用JUC提供的安全容器
-
new CopyOnWriteArraySet<>();
-
使用Collections工具类提供的容器
import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.UUID; /** * 同理可证:java.util.currentModifyException * 1、使用工具类 Collections.synchronizedSet(new HashSet<>()); * */ public class SetTest { public static void main(String[] args) { // Set<String> set = new HashSet<>(); Set<String> set = Collections.synchronizedSet(new HashSet<>()); 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(); } } }
使用JUC提供的CopyOnWriteArraySet
import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; /** * 同理可证:java.util.currentModifyException * 1、使用工具类 Collections.synchronizedSet(new HashSet<>()); * 2、使用JUC提供的:new CopyOnWriteArraySet<>(); */ public class SetTest { public static void main(String[] args) { // Set<String> set = new HashSet<>(); // Set<String> set = Collections.synchronizedSet(new HashSet<>()); 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(); } } }
HashSet的底层
底层描述
很简单就是,就是new了一个HashMap,将HashMap的键用于存储数据;
所以Set容器的特征:就是无序、不可重复的容器
查看源码
第一步查看构造
1、查看源码,进入HashSet类
2、可以很清楚的看到 new 了一个 HashMap
第二步查看add
方法
1、进入add方法
2、找到HashSet
重写的add
方法
3、看到没有,就是将元素put到map容器的键中
4、map中value的值就是一个常量,不会改变的
Map不安全
HashMap底层原理暂时待学习
单线程和多线程下操作Map
单线程下操作Map
import java.util.HashMap; import java.util.Map; import java.util.UUID; public class MapTest { public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); for (int i = 0; i < 10; i++) { map.put(String.valueOf(i), UUID.randomUUID().toString().substring(0, 5)); } map.entrySet().forEach(System.out::println); } }
多线程下操作Map
-
会存在并发问题
import java.util.HashMap; import java.util.Map; import java.util.UUID; public class MapTest { public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5)); System.out.println(map); }, String.valueOf(i)).start(); } } }
解决方法
有两种:
因为Set没有替代的容器。
-
使用工具类
-
Collections.synchronizedMap(new HashMap<>());
-
-
使用JUC提供的安全容器
-
new ConcurrentHashMap<>()
-
使用Collections工具类提供的容器
import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; public class MapTest { public static void main(String[] args) { // Map<String, Object> map = new HashMap<>(); Map<String, Object> map = Collections.synchronizedMap(new HashMap<>()); for (int i = 0; i < 30; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5)); System.out.println(map); }, String.valueOf(i)).start(); } } }
使用JUC提供的ConcurrentHashMap
import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class MapTest { public static void main(String[] args) { // Map<String, Object> map = new HashMap<>(); // Map<String, Object> map = Collections.synchronizedMap(new HashMap<>()); Map<String, Object> map = new ConcurrentHashMap<>(); for (int i = 0; i < 30; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5)); System.out.println(map); }, String.valueOf(i)).start(); } } }