前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >那些年应该相识的线程安全集合们

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

作者头像
程序员小强
发布2019-09-20 17:01:40
4160
发布2019-09-20 17:01:40
举报

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

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

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

- CopyOnWriteArrayList

- BlockingQueue/BlockingDeque

CopyOnWriteArryList

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

代码语言:javascript
复制
    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)                
                newElements = Arrays.copyOf(elements, len + 1);            
            else {                
                newElements = 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

代码语言:javascript
复制
    public E get(int index) {        
        return get(getArray(), index);
   }

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

BlokingQueue/BlockingDeque

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

代码语言:javascript
复制
public interface BlockingQueue<E> extends Queue<E> {
}
代码语言:javascript
复制
public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
}

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

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

  • ArrayBlockingQueue
  • DelayQueue
  • LinkedBlockingQueue
  • PriorityBlockingQueue

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

  • LinkedBlockingDeque

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

  • 链表结构(动态数组)
  • 通过ReentrantLock实现锁
  • 利用Condition实现队列的阻塞等待,唤醒

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

代码语言:javascript
复制
    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面试中非常常见的问题,需要专门的篇幅来介绍。在此不赘述。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-01-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MoziInnovations 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档