上篇推文介绍了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)
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
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接口的常用实现有以下几种:
BlockingDeque接口的常用实现有以下几种:
其实两个队列从实现思想上比较容易理解,有以下特点:
下面以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面试中非常常见的问题,需要专门的篇幅来介绍。在此不赘述。
本文分享自 MoziInnovations 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!