06 002【JUC】怎样将线程不安全的集合类替换为线程安全的类?主要涉及写时复制(CopyOnWrite)

由ArrayList引发的一系列问题

写在前面:务必牢牢记忆java.util.ConcurrentModificationException、CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap

  1. new ArrayList()底层new了什么?数组
  2. 底层什么类型?Object 想想list.add 能装什么 啥都能装
  3. 以Java8为例初始值是多少?开始为空 add后10
  4. 存25个元素可以吗? 可以 ,底层扩容,怎么扩的,10》15,扩原值的一半
  5. 有没有看过源码,怎么搬家的?Arrays.copyOf()
  6. 第二次扩容扩到多少?22 hashmap扩容是原值的一倍16》32
  7. 7.ArrayList线程安全还是不安全?不安全
  8. 举例线程不安全的代码?
  9. Vector是线程安全的,为什么还要有ArrayList呢? 并发性和一致性本身就是相左的,以前没太大并发,锁表,只能一个线程操作
  10. 线程不安全怎么解决?
  11. 为什么写时复制安全?
package com.magic.juc0117;

/**
 * @Description
 * @Author Chelsea
 * @Date 2020/1/17 17:18
 */
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;

/**
 * 1.故障现象
 * java.util.ConcurrentModificationException
 * 2.导致原因
 * 多线程并发争抢同一个资源类并且没有加锁
 * 3.解决方法
 * ① List list = new Vector<>();//源码加锁synchronized(重锁)
 * 根据效率和安全性选择,电商ArrayList不多
 * 不许用vector,请用②解决
 * ②List list = Collections.synchronizedList(new ArrayList<>());
 * 如果②也不让用
 * ③List list = new CopyOnWriteArrayList();//写时复制
 *
 * 4.优化建议(同样的错误不能犯2次)
 */

/**
 * 一个常问的题目:Collection 和 Collections有什么区别?
 * Collection 接口 Collections集合接口的工具类
 */
public class NotSafeDemo03 {

    public static void main(String[] args) {
        listNotSafe();
//同理HashSet、HashMap也是线程不安全的,JUC也提供了对应的安全的类
//        Set set = new HashSet<>();
        Set<String>  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();

        }
//HashMap   ConcurrentHashMap

    }

    public static void listNotSafe(){
//        List list = new ArrayList();
//        list.add("a");
//        list.add("a");
//        list.add("a");
//        list.forEach(System.out::println);  这样不会不安全只有一个线程,下面for循环会异常ConcurrentModificationException
// 换成下面三种方式时异常解决
//        List list = new Vector<>();//源码加锁synchronized
//        List list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList();//写时复制


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

        }
    }


    /**
     * 1.HashSet底层数据结构  HashMap  也不安全
     * 选择CopyOnWriteArraySet
     */

    /**
     *  HashMap
     *  ConcurrentHashMap
     */
}

回答最开始的问题,为什么写时复制安全?
下面是CopyOnWriteArrayList类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();
    }
}

学习整理于JUC.

你可能感兴趣的:(juc)