1.集合的线程不安全示例

private static void listNotSafe() {
    List<String> list= new ArrayList<>();//new CopyOnWriteArrayList<>(); //Collections.synchronizedList(new ArrayList<>());  //new Vector<>();// new ArrayList<>();
    for (int i = 0; i <100 ; i++) {
        new Thread(()->{
            list.add(UUID.randomUUID().toString().substring(0,8));
            System.out.println(list);
        },String.valueOf(i)).start();
    }
}

ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常并发修改异常

2.不安全的原因:ArrayList中add方法不是线程安全的

看ArrayList的源码
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
没有synchronized线程不安全

3.解决办法

3.1    使用Vector:

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
有synchronized线程安全

3.2    使用Colletions中的synchronizedList等方法

类似有:

synchronizedList()
synchronizedSet()
synchronizedMap()

3.3    写时复制 CopyOnWriteArrayList

CopyOnWriteArrayList<>()
CopyOnWriteArraySet<>()
ConcurrentHashMap<>();

4.    写时复制原理

不加锁性能提升出错误,加锁数据一致性能下降

4.1    CopyOnWriteArrayList定义

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

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
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();
    }
}

4.2    扩展类比

HashSet:

Set<String> set = new HashSet<>();//线程不安全

Set<String> set = new CopyOnWriteArraySet<>();//线程安全 
HashSet底层数据结构是什么?    HashMap  ?
但HashSet的add是放一个值,而HashMap是放K、V键值对

HashSet使用HashMap实现,默认值为Object

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

private static final Object PRESENT = new Object();

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

HashMap:

Map<String,String> map = new HashMap<>();//线程不安全

Map<String,String> map = new ConcurrentHashMap<>();//线程安全

5.    示例代码

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

/*
* 题目:请举例说明集合类是不安全的
1.故障现象
并发修改异常:java.util.ConcurrentModificationException
2.导致原因

3.解决方案
3.1.使用Vector,Vector add方法加了 synchronized
3.2.Collections.synchronizedList(new ArrayList<>())
3.3.CopyOnWriteArrayList<>()
4.优化建议

*
* */
public class ListNotSafeDemo {
    public static void main(String[] args) {
        listNotSafe();

    }

    private static void MapNotSafe() {
        Map<String,String> map=new ConcurrentHashMap<>(); //Collections.synchronizedMap()//new HashMap<>();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }

    private static void setNotSafe() {
        Set<String> list=new CopyOnWriteArraySet<>(); //Collections.synchronizedSet(); //new HashSet<>();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }

    private static void listNotSafe() {
        List<String> list= new ArrayList<>();//new CopyOnWriteArrayList<>(); //Collections.synchronizedList(new ArrayList<>());  //new Vector<>();// new ArrayList<>();
        for (int i = 0; i <100 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}
03-24 09:38
查看更多