题出问题
咱们晓得 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<>();