高并发探索(九):并发容器 J.U.C - 线程安全的集合与Map

原创 2018年04月15日 18:21:22

概述

Java并发容器JUC是三个单词的缩写。是JDK下面的一个包名。即Java.util.concurrency。
上一节我们介绍了ArrayList、HashMap、HashSet对应的同步容器保证其线程安全,这节我们介绍一下其对应的并发容器。

ArrayList –> CopyOnWriteArrayList

CopyOnWriteArrayList 写操作时复制,当有新元素添加到集合中时,从原有的数组中拷贝一份出来,然后在新的数组上作写操作,将原来的数组指向新的数组。整个数组的add操作都是在锁的保护下进行的,防止并发时复制多份副本。读操作是在原数组中进行,不需要加锁

  • 缺点:
    1.写操作时复制消耗内存,如果元素比较多时候,容易导致young gc 和full gc。
    2.不能用于实时读的场景.由于复制和add操作等需要时间,故读取时可能读到旧值。
    能做到最终一致性,但无法满足实时性的要求,更适合读多写少的场景。
    如果无法知道数组有多大,或者add,set操作有多少,慎用此类,在大量的复制副本的过程中很容易出错。

  • 设计思想:
    1.读写分离
    2.最终一致性
    3.使用时另外开辟空间,防止并发冲突

  • 源码分析

//构造方法
public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements;//使用对象数组来承载数据
    if (c.getClass() == CopyOnWriteArrayList.class)
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    else {
        elements = c.toArray();
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elements.getClass() != Object[].class)
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
    }
    setArray(elements);
}

//添加数据方法
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);//复制当前数组并且扩容+1
        newElements[len] = e;//将要添加的数据放入新数组
        setArray(newElements);//将原来的数组指向新的数组
        return true;
    } finally {
        lock.unlock();
    }
}

//获取数据方法,与普通的get没什么差别
private E get(Object[] a, int index) {
    return (E) a[index];
}
HashSet –> CopyOnWriteArraySet
  • 它是线程安全的,底层实现使用的是CopyOnWriteArrayList,因此它也适用于大小很小的set集合,只读操作远大于可变操作。因为他需要copy整个数组,所以包括add、remove、set它的开销相对于大一些。
  • 迭代器不支持可变的remove操作。使用迭代器遍历的时候速度很快,而且不会与其他线程发生冲突。
  • 源码分析:
//构造方法
public CopyOnWriteArraySet() {
    al = new CopyOnWriteArrayList<E>();//底层使用CopyOnWriteArrayList
}

//添加元素方法,基本实现原理与CopyOnWriteArrayList相同
private boolean addIfAbsent(E e, Object[] snapshot) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] current = getArray();
        int len = current.length;
        if (snapshot != current) {//添加了元素去重操作
            // Optimize for lost race to another addXXX operation
            int common = Math.min(snapshot.length, len);
            for (int i = 0; i < common; i++)
                if (current[i] != snapshot[i] && eq(e, current[i]))
                    return false;
            if (indexOf(e, current, common, len) >= 0)
                    return false;
        }
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}
TreeSet –> ConcurrentSkipListSet

它是JDK6新增的类,同TreeSet一样支持自然排序,并且可以在构造的时候自己定义比较器。

  • 同其他set集合,是基于map集合的(基于ConcurrentSkipListMap),在多线程环境下,里面的contains、add、remove操作都是线程安全的。
  • 多个线程可以安全的并发的执行插入、移除、和访问操作。但是对于批量操作addAll、removeAll、retainAll和containsAll并不能保证以原子方式执行,原因是addAll、removeAll、retainAll底层调用的还是contains、add、remove方法,只能保证每一次的执行是原子性的,代表在单一执行操纵时不会被打断,但是不能保证每一次批量操作都不会被打断。在使用批量操作时,还是需要手动加上同步操作的。
  • 不允许使用null元素的,它无法可靠的将参数及返回值与不存在的元素区分开来。
  • 源码分析:
//构造方法
public ConcurrentSkipListSet() {
    m = new ConcurrentSkipListMap<E,Object>();//使用ConcurrentSkipListMap实现
}
HashMap –> ConcurrentHashMap
  • 不允许空值,在实际的应用中除了少数的插入操作和删除操作外,绝大多数我们使用map都是读取操作。而且读操作大多数都是成功的。基于这个前提,它针对读操作做了大量的优化。因此这个类在高并发环境下有特别好的表现。
  • ConcurrentHashMap作为Concurrent一族,其有着高效地并发操作,相比Hashtable的笨重,ConcurrentHashMap则更胜一筹了。
  • 在1.8版本以前,ConcurrentHashMap采用分段锁的概念,使锁更加细化,但是1.8已经改变了这种思路,而是利用CAS+Synchronized来保证并发更新的安全,当然底层采用数组+链表+红黑树的存储结构。
  • 源码分析:推荐参考chenssy的博文:J.U.C之Java并发容器:ConcurrentHashMap
TreeMap –> ConcurrentSkipListMap
  • 底层实现采用SkipList跳表
  • 曾经有人用ConcurrentHashMap与ConcurrentSkipListMap做性能测试,在4个线程1.6W的数据条件下,前者的数据存取速度是后者的4倍左右。但是后者有几个前者不能比拟的优点:
    1、Key是有序的
    2、支持更高的并发,存储时间与线程数无关
安全共享对象策略
  • 线程限制:一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改
  • 共享只读:一个共享只读的U帝乡,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它
  • 线程安全对象:一个线程安全的对象或者容器,在内部通过同步机制来保障线程安全,多以其他线程无需额外的同步就可以通过公共接口随意访问他
  • 被守护对象:被守护对象只能通过获取特定的锁来访问。
版权声明:本文为博主原创文章,若涉及侵权或需要转载请联系我yinjiaxing_web@163.com。 https://blog.csdn.net/jesonjoke/article/details/79947616

java并发容器(Map、List、BlockingQueue)详解

Java库本身就有多种线程安全的容器和同步工具,其中同步容器包括两部分:一个是Vector和Hashtable。另外还有JDK1.2中加入的同步包装类,这些类都是由Collections.synchr...
  • li295214001
  • li295214001
  • 2015-08-19 16:06:08
  • 2572

高并发下的线程安全实现——互斥同步

高并发下的线程安全实现——互斥同步好久没来csdn上写博客了,去年(16年)来到杭州后,忙得沉淀的时间都没有了,这段时间空闲下来,慢慢补上! 线程允许多个活动同时进行,并发下有很多东西可能出错,比如...
  • Tian779278804
  • Tian779278804
  • 2017-07-25 10:48:17
  • 411

专用于高并发的map类-----Map的并发处理(ConcurrentHashMap)

oncurrentModificationException 在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException...
  • dingxingmei
  • dingxingmei
  • 2014-12-17 14:34:38
  • 10115

【JDK】:Java容器框架——同步容器与并发容器

前面的文章中详细介绍了Java的容器框架,在此基础上,本文对Java中的同步容器与并发容器做一些介绍。fail-fast机制快速报错机制(fail-fast)能够防止多个进程同时修改同一个容器的内容。...
  • u011080472
  • u011080472
  • 2016-05-15 21:00:58
  • 2583

Android List,Set,Map集合安全 集合区别 并发集合类性能分析

对于Android开发者来说深入了解Java的集合类很有必要主要是从Collection和Map接口衍生出来的,目前主要提供了List、Set和 Map这三大类的集合 Collection接口主要有...
  • WHB20081815
  • WHB20081815
  • 2017-04-20 19:16:46
  • 1742

【死磕Java并发】-----J.U.C之Java并发容器:ConcurrentLinkedQueue

原文出处http://cmsblogs.com/ 『chenssy』 要实现一个线程安全的队列有两种方式:阻塞和非阻塞。阻塞队列无非就是锁的应用,而非阻塞则是CAS算法的应用。下面我们就开始一个非阻塞...
  • chenssy
  • chenssy
  • 2017-07-08 21:26:10
  • 6709

线程安全的并发集合类

1.简述 实现一个线程安全的集合并不难,难的是尽可能的消除并发带来的竞争瓶颈,提升效率。 所以JDK自带的并发类的意义与技术含量在于这里。 2.List 没有通用的实现类,只有一个使用场景受限...
  • chuchus
  • chuchus
  • 2015-12-10 16:00:10
  • 1034

Java并发容器大合集

概述        java.util包中的大部分容器都是非线程安全的,若要在多线程中使用容器,你可以使用Collections提供的包装函数:synchronizedXXX,将普通容器变成线程安全的...
  • u010425776
  • u010425776
  • 2017-02-06 10:24:13
  • 2110

Java并发容器并发集合

BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会...
  • wtyvhreal
  • wtyvhreal
  • 2015-09-04 23:23:17
  • 742

java集合中的线程安全问题

Collection是无序的,允许元素重复 List集合是有序的,允许元素重复 Set集合   HashSet、AbstractSet集合是无序的,TreeSet集合是有序的,不允许元素重复,如果有相...
  • rururu2211785113
  • rururu2211785113
  • 2017-02-24 19:48:01
  • 909
收藏助手
不良信息举报
您举报文章:高并发探索(九):并发容器 J.U.C - 线程安全的集合与Map
举报原因:
原因补充:

(最多只允许输入30个字)