题出问题
我们知道ArrayList是线程不安全,请编写一个不安全的案例并给出解决方案?
单线程环境
单线程环境的ArrayList是不会有问题的
public class ArrayListNotSafeDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
for(String element : list) {
System.out.println(element);
}
}
}
多线程环境
为什么多线程环境下ArrayList是线程不安全的?因为在进行写操作(add方法)的时候,方法上为了保证并发性,没有添加synchronized修饰。
当我们同时启动30个线程去操作List的时候
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (long i = 0; i < 30; i++) {
new Thread(() -> {
list.add("hello");
list.add("world");
list.add("java");
System.out.println(list);
}).start();
}
}
}
我们运行发现报了java.util.ConcurrentModificationException(并发修改的异常)
解决方案
方案一:Vector
采用Vector实现线程安全,Vector在方法上加了锁,即synchronized
这样就每次只能够一个线程进行操作,所以不会出现线程不安全的问题,但是因为加锁了,导致并发性下降。
方案二:Collections.synchronized()
List<String> list = Collections.synchronizedList(new ArrayList<>());
采用Collections集合工具类,在ArrayList外面包装一层 同步 机制
方案三:采用JUC里面的方法
CopyOnWriteArrayList:写时复制,主要是一种读写分离的思想。
- 就是写的时候,把ArrayList扩容一个出来,然后把值填写上去,在通知其他的线程,ArrayList的引用指向扩容后的。
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
查看add方法源码
首先需要加锁
final ReentrantLock lock = this.lock; lock.lock();
然后在末尾扩容一个单位
Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1);//扩容一个单位,复制到新数组
然后在把扩容后的空间,填写上需要add的内容
newElements[len] = e;
- 最后把内容set到Array中
应用场景:一般用于黑/白名单,这样的地方,标准的读多写少场景
HashSet线程不安全
用CopyOnWriteArraySet
(底层还是用的CopyOnWriteArrayList)
HashMap线程不安全
- 使用
Collections.synchronizedMap(new HashMap<>())
使用ConcurrentHashMap
Map<String, String> map = new ConcurrentHashMap<>();
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。