4.1 ArrayList

4.1.1 故障现象

  1. package s02.e04;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.UUID;
  5. /**
  6. * 集合类不安全的问题
  7. * ArrayList
  8. */
  9. public class ContainerNotSafeDemo {
  10. public static void main(String[] args) {
  11. List<String> list = new ArrayList<>();
  12. for (int i = 1; i <= 30; i++) {
  13. new Thread(() -> {
  14. list.add(UUID.randomUUID().toString().substring(0, 8));
  15. System.out.println(list);
  16. }, String.valueOf(i)).start();
  17. }
  18. }
  19. }

image.png

4.1.2 导致原因

并发争抢修改导致,参考我们的花名册签名情况。
一个人正在写入,另外一个同学过来抢夺,导致数据不一致异常。并发修改异常。

4.1.3 解决方案

  1. 使用 Vector 类

    1. List<String> list = new Vector<>();
  2. 使用 Collections 类

    1. List<String> list = Collections.synchronizedList(new ArrayList<>());
  3. 写时复制

    1. List<String> list = new CopyOnWriteArrayList<>();

    4.1.4 写时复制

    CopyOnWrite 容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器 object[] 添加,而是先将当前容器 object[] 进行 copy,复制出一个新的容器 object[ ] newElements,然后新的容器 object[] newELements 里添加元素,添加完元素之后,再将原容器的引用指向新的容器 setArray(newELements);。这样做的好处是可以对 CopyOnwrite 容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以 Copyonwrite 容器也是一种读写分离的思想,读和写不同的容器。

    1. public boolean add(E e) {
    2. final ReentrantLock lock = this.lock;
    3. Lock.lock();
    4. try {
    5. Object[] elements = getArray();
    6. int len = elements.length;
    7. Object[] newELements = Arrays.copyOf(elements, len + 1);
    8. newElements[len] = e;
    9. setArray(newELements);
    10. return true;
    11. } finally {
    12. lock.unlock();
    13. }
    14. }

    4.2 Set

    4.2.1 故障现象

    ```java package s02.e04;

import java.util.HashSet; import java.util.Set; import java.util.UUID;

/**

  • 集合类不安全的问题
  • ArrayList */ public class ContainerNotSafeDemo { public static void main(String[] args) {
    1. Set<String> set = new HashSet<>();
    2. for (int i = 1; i <= 30; i++) {
    3. new Thread(() -> {
    4. set.add(UUID.randomUUID().toString().substring(0, 8));
    5. System.out.println(set);
    6. }, String.valueOf(i)).start();
    7. }
    } } ``` image.png

    4.2.2 解决方案

  1. 使用 Collections 类

    1. Set<String> set = Collections.synchronizedSet(new HashSet<>());
  2. 写时复制

    1. Set<String> set = new CopyOnWriteArraySet<>();

    image.png
    注意其底层仍然是 CopyOnWriteArrayList

    4.2.3 补充

  3. HashSet 底层用的是 HashMap

image.png

  1. add 方法的参数是 HashMap 的 key,value 为固定值

image.png
image.png

4.3 Map

4.3.1 故障现象

  1. package s02.e04;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import java.util.UUID;
  5. /**
  6. * 集合类不安全的问题
  7. * ArrayList
  8. */
  9. public class ContainerNotSafeDemo {
  10. public static void main(String[] args) {
  11. Map<String, String> map = new HashMap<>();
  12. for (int i = 1; i <= 30; i++) {
  13. new Thread(() -> {
  14. map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
  15. System.out.println(map);
  16. }, String.valueOf(i)).start();
  17. }
  18. }
  19. }

image.png

4.3.2 解决方案

ConcurrentHashMap

  1. Map<String, String> map = new ConcurrentHashMap<>();