集合类不安全问题

学习课程记录笔记

1、ArrayList线程安全问题

ArrayList不是线程安全类,在多线程同时写的情况下,会抛出java.util.ConcurrentModificationException异常。

private static void listNotSafe() {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(Thread.currentThread().getName()+"\t"+list);
            },String.valueOf(i)).start();
        }
    }

解决方法

  1. 使用VectorArrayList所有方法加synchronized,太重)。
  2. 使用Collections.synchronizedList()转换成线程安全类。
  3. 使用java.concurrent.CopyOnWriteArrayList(推荐)。
CopyOnWriteArrayList

这是JUC的类,通过写时复制来实现读写分离。比如其add()方法,就是先复制一个新数组,长度为原数组长度+1,然后将新数组最后一个元素设为添加的元素。

源码分析
 /**
     * 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();
        }
    }

ArrayList的初始化与扩容机制:
初始化 源码:
       private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
扩容机制:
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
(oldCapacity >> 1); 也就是按照当前容量的一半扩容

2、Set线程安全问题

跟List类似,HashSetTreeSet都不是线程安全的,与之对应的有CopyOnWriteSet这个线程安全类。这个类底层维护了一个CopyOnWriteArrayList数组。

源码
 /**
     * Creates an empty set.
     */
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }

3、HashSet和HashMap

HashSet底层是用HashMap实现的。既然是用HashMap实现的,那HashMap.put()需要传两个参数,而HashSet.add()传一个参数,这是为什么?实际上HashSet.add()就是调用的HashMap.put(),只不过Value被写死了,是一个private static final Object对象。

源码
 /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }


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

 // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

4、Map

HashMap不是线程安全的,Hashtable是线程安全的,但是跟Vector类似,太重量级(synchronized修饰)。所以也有类似CopyOnWriteMap,只不过叫ConcurrentHashMap

public class ContainerNotSafeDemo {
    public static void main(String[] args) throws Exception{
        listNotSafe();
        listSafe();
        setNoSafe();
        setSafe();
        mapNotSafe();
        mapSafe();
    }
    private static void mapSafe() throws Exception{
        TimeUnit.SECONDS.sleep(5);
        System.out.println("---------------Map线程安全---------------");
        Map<String,String> 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(Thread.currentThread().getName()+"\t"+map);
            },String.valueOf(i)).start();
        }
    }
    private static void mapNotSafe() {
        System.out.println("---------------Map线程不安全---------------");
        Map<String,String> map = 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(Thread.currentThread().getName()+"\t"+map);
            },String.valueOf(i)).start();
        }
    }

    private static void setSafe()throws Exception {
        Set<String> set = new CopyOnWriteArraySet<>();
        TimeUnit.SECONDS.sleep(5);
        System.out.println("---------------Set线程安全---------------");
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(Thread.currentThread().getName()+"\t"+set);
            },String.valueOf(i)).start();
        }
    }
    private static void setNoSafe() {
        System.out.println("---------------Set线程不安全---------------");
        Set<String> set = new HashSet<>();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(Thread.currentThread().getName()+"\t"+set);
            },String.valueOf(i)).start();
        }
    }
    private static void listNotSafe() {
        System.out.println("---------------List线程不安全---------------");
        List<String> list = new ArrayList<String>();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(Thread.currentThread().getName()+"\t"+list);
            },String.valueOf(i)).start();
        }
    }
    private static void listSafe()throws Exception {

        // List<String> list = new ArrayList<String>();
        // List<String> strings = Collections.synchronizedList(list);
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        TimeUnit.SECONDS.sleep(5);
        System.out.println("---------------List线程安全---------------");
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(Thread.currentThread().getName()+"\t"+list);
            },String.valueOf(i)).start();
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值