java面试-集合类不安全问题及解决方案
阅读原文时间:2023年07月08日阅读:1

一、List

1、代码演示

public class ArrayListNotSafeDemo {

public static void main(String\[\] args) {  
    List<String> list = new ArrayList<>();  
    for (int i = 1; i <= 30; i++) {  
        new Thread(() -> {  
            //Constructs an empty list with an initial capacity of ten.  
            list.add(UUID.randomUUID().toString().substring(0, 8));  
            System.out.println(list);  
        }, String.valueOf(i)).start();  
    }  
}  

}

2、故障现象

java.util.ConcurrentModificationException

3、导致原因

一个线程正在写,另一线程过来抢夺,导致数据不一致,即并发修改导致的异常

4、解决方案

  • new Vector<>()
  • Collections.synchronizedList()
  • new CopyOnWriteArrayList<>()

在读多写少的时候推荐使用 CopeOnWriteArrayList 这个类

写时复制,读写分离的思想 好处:读操作完全无锁

使用场景 :写操作非常少的场合,能容忍读写的短暂不一致。

CopyOnWriteArrayList迭代器是只读的,不支持增删改。

5、 CopyOnWriteArrayList源码解读:

public boolean add(E e) {  
    final ReentrantLock lock = this.lock;  
    lock.lock();  
    try {  
        Object\[\] elements = getArray();  
        int len = elements.length;  
        Object\[\] newElements = Arrays.copyOf(elements, len + 1);  
        newElements\[len\] = e;  
        setArray(newElements);  
        return true;  
    } finally {  
        lock.unlock();  
    }  
}

二、Set

1、代码演示:

public class HashSetNotSafeDemo {

public static void main(String\[\] args) {  
    Set<String> list = new HashSet<>();  
    for (int i = 1; i <= 30; i++) {  
        new Thread(() -> {  
            list.add(UUID.randomUUID().toString().substring(0, 8));  
            System.out.println(list);  
        }, String.valueOf(i)).start();  
    }  
}  

}

2、解决方案:

  • Collections.synchronizedSet()
  • new CopyOnWriteArraySet<>()

3、CopyOnWriteArraySet底层源码:

底层使用CopyOnWriteArrayList

public CopyOnWriteArraySet() {  
    al = new CopyOnWriteArrayList<E>();  
}  

4、HashSet底层源码

HashSet的key是你add()的值,value是一个叫PRESENT Object类型的常量,即HashSet只关心key

public HashSet() {  
    map = new HashMap<>();  
}

private static final Object PRESENT = new Object();

public boolean add(E e) {  
    return map.put(e, PRESENT)==null;  
}  

三、Map  

1、代码演示:

public class HashMapNotSafeDemo {

public static void main(String\[\] args) {  
    Map<String, String> map = new HashMap<>();  
    for (int i = 1; i <= 30; i++) {  
        new Thread(() -> {  
            map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));  
            System.out.println(map);  
        }, String.valueOf(i)).start();  
    }  
}  

}

2、解决方案

  • Collections.synchronizedMap()
  • new ConcurrentHashMap<>();