4、线程不安全--集合类

1、证明ArrayList线程不安全

class ListNoSafe{
    private List list = new ArrayList<>();
    public void add(){
        list.add(UUID.randomUUID().toString().substring(0,3));
        System.out.println(list);
    }
}

public class ThreadArrayList {
    public static void main(String[] args) {
        ListNoSafe listNoSafe = new ListNoSafe();
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                for (int j = 0; j < 5; j++) {
                    listNoSafe.add();
                }
            },String.valueOf(i)).start();
        }

    }
}

=========或者直接如下=========
List list = new ArrayList<>();
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();
        }

多次运行会抛出java.util.ConcurrentModificationException异常
分析ArrayList源码

//可知其是线程不安全的
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

2、使用Vector


Vector源码

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

修改代码为

List list = new Vector();

3、使用Collections

private List list = Collections.synchronizedList(new ArrayList<>());

4、使用JUC包下的方法 - - 写时复制


List list = new CopyOnWriteArrayList<>();

5、写时复制

首先因为为了保证线程安全,会采用加锁进行解决,但是加锁会导致性能下降

  • CopyOnWriteArrayList定义
    CopyOnWriteArrayList是arraylist的一种线程安全变体,
    其中所有可变操作(add、set等)都是通过生成底层数组的新副本来实现的。


5.1、CopyOnWrite理论
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();
    }
}

CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接在往当前容器Object[]添加,而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素

添加元素后,再将原容器的引用指向新的容器setArray(newElements),这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何的元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写在不同的容器

6、HashSet

首先HashSet底层数据结构是HashMap,往HashSet添加元素,存放在K中,在V中保存常量Object对象

public HashSet() {
    map = new HashMap<>();
}
 
private static final Object PRESENT = new Object();
 
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

线程

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

Set set = new CopyOnWriteArraySet<>();//线程安全

7、HashMap

线程

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

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

8、代码


/**
 * 请举例说明集合类是不安全的
 */
public class NotSafeDemo {
    public static void main(String[] args) {

        Map map = new ConcurrentHashMap<>();
        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 setNoSafe() {
        Set set = new CopyOnWriteArraySet<>();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }

    private static void listNoSafe() {
        //        List list = Arrays.asList("a","b","c");
        //        list.forEach(System.out::println);
        //写时复制
        List list = new CopyOnWriteArrayList<>();
        // new CopyOnWriteArrayList<>();
        //Collections.synchronizedList(new ArrayList<>());
        //new Vector<>();//new ArrayList<>();

        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();
                }
    }


}

你可能感兴趣的:(4、线程不安全--集合类)