文章目录
- 1、ArrayList集合线程安全问题分析
- 2、解决方式一:Vector或synchronizedList( )
- 3、解决方式二:CopyOnWriteArrayList 写时复制
- 4、HashSet集合线程不安全的分析与解决
- 5、HashMap集合线程不安全的分析与解决
1、ArrayList集合线程安全问题分析
对List集合非线程安全的Demo代码:
public class ArrayListDemo {public static void main(String[] args) {List<String> list = new ArrayList<>();//多个线程同时写入List集合for (int i = 0; i < 10; i++) {new Thread(() -> {//加元素list.add(UUID.randomUUID().toString().substring(0,8));//遍历输出集合System.out.println(list);},String.valueOf(i)).start();}}
}
运行:
ConcurrentModificationException异常,是在多线程环境下,当一个线程正在遍历集合,而另一个线程对集合进行了修改操作时,就会抛出这个异常。以ArrayList为例,其add方法源码,未加synchronized关键字:
再点击报错详情,进入抛出异常的方法:
modCount即集合新增的次数,是实际修改次数,而expectedModCount是预期修改次数,它是ArrayList的一个内部类Itr的成员变量,调用iterator()获取迭代器时,内部创建Itr对象,此时,modCount会赋值给expectedModCount:
拿到迭代器对象,要遍历集合时,modCount已经赋值给expectedModCount,而此时其他线程继续add,modCount+1,modCount和expectedModCount就不相等了。
2、解决方式一:Vector或synchronizedList( )
List接口的另一个实现类Vector,其add方法加了关键字,使用它可解决线程安全问题,但很古老了,since1.2,很少用了。
List<String> list = new Vector<>();
//重复代码略
同样一种古老的解决方案,可以用Collections的synchronizedList方法,传入一个有线程安全问题的List,如ArrayList:
List<String> list = Collections.synchronizedList(new ArrayList<>());
3、解决方式二:CopyOnWriteArrayList 写时复制
List<String> list = new CopyOnWriteArrayList<>();
完整demo:
public class ArrayListDemo {public static void main(String[] args) {List<String> list = new CopyOnWriteArrayList<>();//多个线程同时写入List集合for (int i = 0; i < 10; i++) {new Thread(() -> {//加元素list.add(UUID.randomUUID().toString().substring(0,8));//遍历输出集合System.out.println(list);},String.valueOf(i)).start();}}
}
原理是写时复制技术,即:
- 对这个List实现类的集合,可以多线程并发读
- 往集合中写的时候,则只能独立写,先复制一份原来的集合,这个时候读还是读原来的,然后往新集合里面写入新的内容
- 写完后新旧合并,再读时,就读这个合并后的集合
看下源码,再对照着理解写时复制:
4、HashSet集合线程不安全的分析与解决
public class HashSetDemo {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,8));//读System.out.println(set);},String.valueOf(i)).start();}}
}
运行:
解决办法类比上面的List,使用CopyOnWriteArraySet
:
Set<String> set = new CopyOnWriteArraySet<>();
5、HashMap集合线程不安全的分析与解决
public class HashSetDemo {public static void main(String[] args) {Map<String,string> map = new HashMap<>();for (int i = 0; i < 30; i++) {String key = String.valueOf(i);new Thread(() -> {//写入map.put(key,UUID.randomUUID().toString().substring(0,8));//读System.out.println(map);},String.valueOf(i)).start();}}
}
解决办法类比List,用ConcurrentHashMap
:
Map<String,String> map = new ConcurrentHashMap<>();