JUC- 集合不安全

CopyOnwriteArrayList

ArrayList的问题:ConcurrentModificationException 并发修改异常

public class CopyonwriteArraylist_Test {
    public static void main(String[] args) {
        List<String > list=new ArrayList();
        for (int i=0 ;i<=10 ;i++){
            new Thread(()->{list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list); },String.valueOf(i)).start();
        }
    }
}
复制代码

解决方案:

  • List list=new Vector();
  • List list=Collections.synchronizedList();
  • List list=new CopyOnWriteArrayList<>();

为什么CopyOnWriteArrayList可以保证线程安全

public boolean add(E e) {
        final ReentrantLock lock = this.lock; //通过ReentrantLock保证只有一个线程进行修改
        lock.lock();
        try {
        // 写时复制
            Object[] elements = getArray();//获取旧数组
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);//创建一个len+1的新数组,并将旧数组copy过来
            newElements[len] = e; //在新数组末尾添加新元素
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    @SuppressWarnings("unchecked")
    // 读不加锁
    private E get(Object[] a, int index) {
        return (E) a[index];
    }
复制代码

优缺点

  • CopyOnWrite容器,写时复制,是读写分离的思想。
  • 读的时候不加锁,可以并发的读,提高并发性
  • 只有在修改或删除的时候加锁,并且是对新数组进行操作不影响读
  • 由于读不加锁,所以有可能在读的时候有线程对数据进行修改,因而读的是旧数据,即弱一致性
  • 占用内存,在写的时候会创建新对象添加到新容器里,而旧容器的对象还在使用,所以有两份对象内存。内存占用大时,可考虑ConcurrentHashMap

与Vector的区别

  • Vector 是粗暴的加synchronized,并发性低,CopyOnWrire只在修改的时候加锁,读的操作性高

CopyOnwriteArraySet

  • HashSet 本质是Hash Map的key,利用hashmap的key来保证唯一,同时也导致了无序。
  • CopyOnwriteArraySet 却是调用CopyOnwriteArrayList来实现的。
 public HashSet() {
        map = new HashMap<>();
    }
 private static final Object PRESENT = new Object();
 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
复制代码
public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }
public boolean add(E e) {
        return al.addIfAbsent(e);
    }

复制代码

ConcurrentHashMap

  • HashMap 是线程不安全的
  • HaspTable 是线程安全的,但是简单粗暴的加synchronized,效率低下

hashmap.png

ConcurrentHashMap

参考:zhuanlan.zhihu.com/p/31614308

ConcurrentHashMap维护一个segments数组如下,可以说,ConcurrentHashMap是一个二级哈希表。在一个总的哈希表下面,有若干个子哈希表。
YYPBZVX0V@{6NE8FH5~KR1J.png

  • 不同的Segment拥有不同的锁,在保证线程安全的同时降低了锁的粒度,让并发操作效率更高。
  • 同一个Segment读不加锁,写才加锁
  • 1.8之后直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用内置Synchronized和CAS来操作,一把锁只锁住一个链表

参考:blog.csdn.net/qq_41884976…
5T4(T)GG36~WSV8QIFVR}RN.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享