Java集合类不安全问题(HaspMap不安全)


Java中的一些集合类:例如ArrayList、HashSet、HashMap 在并发条件下都是不安全的。一般情况下报的错误是:“Concurrent Modification Exception”

ArrayList

为什么ArrayList 不安全

在并发条件下,一个线程对ArrayList进行写入的时候,还没有写入结束,就被另一个线程抢过来写入就会造成数据不一致的异常,并发修改异常。
例如:A通过在纸上写字,B同学直接将纸抢过来,那么纸上就会有一道痕迹,这就造成了修改异常。

怎么解决ArrayList 不安全问题

一般情况下有三种解决方案:

  1. 使用Vector 类代替ArrayList。Vector 底层的add方法是加了 synchronized锁,虽然可以解决线程不安全问题,但是并发量就会下降,速度低。
  2. 使用Collections.synchronizedList(List)。使用Collections类,Collections在我看来可以解释为Collection的一些工具类,那么有线程不安全问题的话,Collections中有解决线程不安全的辅助方法。
  3. 使用CopyOnWriteArrayList<>()
    上述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();
        }
    }

从上述代码可以看出,在最外层加锁,ReentrantLock,然后内部使用的是写时复制原则:不是在原有的Object[] 上面进行写入,而是首先复制原有的Object[],然后在新的Object[] newElements中进行加入,然后将原有的 Object[] 引用指向新的 newElements。在写入的时候加锁,好处是:读的时候可以并发执行,写入的时候才会加锁。

对于Collections.synchronizedList的一些补充讲解:
可以看到当传入** ArrayList** 的时候,会返回 new SynchronizedRandomAccessList类。
SynchronizedRandomAccessListSynchronizedList有什么区别呢?他们的主要区别就是是否实现了RandomAccess接口。如果实现了RandomAccess接口的话,那么一般使用for循环进行遍历,否则使用迭代器进行遍历。

	public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }

可以看出 SynchronizedRandomAccessList类实现add 、remove等都是使用的父类的方法。下面代码中都是使用的 **synchronized (mutex) **进行上锁,而 mutex从源码中可以看出,它就是指的 **SynchronizedCollection ** 对象本身,所以如果使用 Collections.synchronizedList 来完成并发的效率会很低,因为它实现线程安全是对自己暴力加锁;

	public void add(int index, E element) {
        synchronized (mutex) {list.add(index, element);}
    }
    public E remove(int index) {
        synchronized (mutex) {return list.remove(index);}
    }

HashSet不安全问题

HashSet也是线程不安全的。同理,如果解决不安全问题可以使用:

  1. Collections.synchronizedSet();
  2. CopyOnWriteArraySet
    但是从源代码中可以看出 CopyOnWriteArraySet 底层使用的是CopyOnWriteArrayList实现的。
  private final CopyOnWriteArrayList<E> al;
  public boolean add(E e) {
        return al.addIfAbsent(e);
    }

HashMap

HashMap是不安全的,解决方法有以下两种:

  1. 使用 Collections.synchronizedMap()
  2. 使用 ConcurrentHashMap 类。

ConcurrentHashMap 内部使用的是分段锁解决不安全问题。
ConcurrentHashMap和 HashMap一样内部都是数组+链表,分段锁就是对于数组的某一个元素进行加锁,那么最终的结果就是在 put 方法上面加锁,但是也不是完全的锁住,只在操作数组中某一元素的链表。

 transient volatile Node<K,V>[] table;
 final V putVal(K key, V value, boolean onlyIfAbsent) {
         ...
          Node<K,V> f;
          ...
          synchronized (f) {
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值