搜公众号
推荐 原创 视频 Java开发 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库
Lambda在线 > Oo蒲创oO > 那些年应该相识的线程安全集合们

那些年应该相识的线程安全集合们

Oo蒲创oO 2019-02-11
举报


       上篇推文介绍了List的三种实现其实都不是线程安全的,文章结尾也回答了如何创建线程安全的List,答案是:Collections.synchronizedList。

       接下来小强再后台收到热心童鞋(在此特别感谢并艾特三老师)回复了其他方式List线程安全的实现方式:CopyOnWriteArrayList。

        本文就为大家总结下Concurrent下常用的线程安全集合们。主要包含以下两种类型的集合:

        - CopyOnWriteArrayList

        - BlockingQueue/BlockingDeque



那些年应该相识的线程安全集合们
CopyOnWriteArryList
那些年应该相识的线程安全集合们



         在使用CopyOnWriteArrayList之前,我们先阅读其源码了解下它是如何实现的:

    public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();        
       try
{            Object[] elements = getArray();            int len = elements.length;            
           if (index > len || index < 0)                
               throw
new IndexOutOfBoundsException("Index: "+index+", Size: "+len);            Object[] newElements;            int numMoved = len - index;            
           if
(numMoved == 0)                
               new
Elements = Arrays.copyOf(elements, len + 1);            
           else
{                
               new
Elements = new Object[len + 1];                System.arraycopy(elements, 0, newElements, 0, index);                System.arraycopy(elements, index, newElements, index + 1,                                 numMoved);            }            
           newElements[index] = element;            setArray(newElements);        } finally {            lock.unlock();        }    }

        传统的List在多线程同时读写的时候会抛出java.util.ConcurrentModificationException,而CopyOnWriteArrayList是使用CopyOnWrite(写时复制)技术解决了这个问题,上面代码是向CopyOnWriteArrayList中add方法的实现(向CopyOnWriteArrayList里添加元素),可以发现在添加的时候是需要加锁的,否则多线程写的时候会Copy出N个副本出来。这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更有效。

        读的时候不需要加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的CopyOnWriteArrayList

    public E get(int index) {        
       return
get(getArray(), index);    }

        所以,通过上面的介绍,CopyOnWriteArrayList并发场景更适合于读多写少的场景。


那些年应该相识的线程安全集合们
BlokingQueue/BlockingDeque
那些年应该相识的线程安全集合们

        阻塞队列BlockingQueue是一个接口,BlockingDeque继承了BlockingQueue,代码如下:

public interface BlockingQueue<E> extends Queue<E> {
}
public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
}

        这里两个比较相似的阻塞队列,BlockingQueue和BlockingDeque,两个都是队列,只不过前者只能一端出一端入,后者则可以两端同时出入,并且他们的实现类都是结构改变线程安全的队列。

        BlockingQueue接口的常用实现有以下几种:

    • ArrayBlockingQueue

    • DelayQueue

    • LinkedBlockingQueue

    • PriorityBlockingQueue

        

        BlockingDeque接口的常用实现有以下几种:

    • LinkedBlockingDeque



        其实两个队列从实现思想上比较容易理解,有以下特点:

      • 链表结构(动态数组)

      • 通过ReentrantLock实现锁

      • 利用Condition实现队列的阻塞等待,唤醒

        下面以ArrayBlockingQueue的构造方法举例来了解以上特点:

    public ArrayBlockingQueue(int capacity, boolean fair) {        
       if
(capacity <= 0)            
           throw new IllegalArgumentException();        
       this.items = new Object[capacity];        lock = new ReentrantLock(fair);        notEmpty = lock.newCondition();        notFull =  lock.newCondition();    }
结语

        其实还有一种很常见的线程安全结合ConcurrentHashMap。它是一种支持高并发、高吞吐量的线程安全HashMap实现,其中,在java8中对ConcurrentHashMap的结构进行了很大的改造。

        以后的文章,小强可以详细介绍这部分内容,因为这部分属于JAVA面试中非常常见的问题,需要专门的篇幅来介绍。在此不赘述。


往期精彩


                           好看的你,记得给本文一个“好看”哦

版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《那些年应该相识的线程安全集合们》的版权归原作者「Oo蒲创oO」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

关注Oo蒲创oO微信公众号

Oo蒲创oO微信公众号:pc_1980

Oo蒲创oO

手机扫描上方二维码即可关注Oo蒲创oO微信公众号

Oo蒲创oO最新文章

精品公众号随机推荐

举报