并发包容器详解

并发容器是指在高并发应用程序的使用过程中,这些容器是线程安全的,而且在高并发的程序中运行它们会有高效的性能表现。

1、链表

     跳表是一种多层链表,其查询性能低于有序顺序表,但是比其他链表的性能高很多。跳表也是Redis的主要数据结构之一。

2、BlockingQueue(阻塞队列)

    BlockingQueue是指其中的元素数量存在界限,当队列已满时,对队列进行写入操作的线程将被阻塞挂起,当队列为空时,对队列进行读取操作的线程将被阻塞挂起。BlockingQueue除了LinkedTransferQueue之外,内部实现主要依赖显式锁Lock及其与之关联的Condition。BlockingQueue在线程池服务中主要作为对线程任务存取容器。

     ArrayBlockingQueue是一个基于数组结构实现的FIFO阻塞队列,在构造该队列时需要指定队列中最大元素数量。由于阻塞队列常被应用到高并发多线程的环境中,因此两个线程同时对头部数据进行获取时,必然有一个线程进入阻塞。ArrayBlockingQueue提供了阻塞式和非阻塞式的添加获取方法,阻塞式在添加/获取元素不能完成时,会等待队列有空间或元素时,继续执行;非阻塞式在添加/获取元素不能玩呢称时,会直接返回失败信息。

     PriorityBlockingQueue优先级阻塞队列是一个“无边界”阻塞队列,与ArrayBlockingQueue有一致的API,但是具体实现差异很大,不过PriorityBlockingQueue也是线程安全的类,适用于并发多线程的情况。PriorityBlockingQueue是一个有排序无边界的队列,初始化时需要指定初始容量,不存在阻塞写方法,但是读取元素的方法与ArrayBlockingQueue类似。

      LinkedBlockingQueue是基于数组实现的FIFO有边界队列,是一种链表实现。该容器的最大边界是通过构造函数指定的,默认是整型的最大值。

      DelayQueue是一个无边界的“阻塞”队列,存入DelayQueue中的数据元素会被延迟单位时间后才能消费。在DelayQueue中,元素会根据优先级进行排序,这种排序可以是基于数据元素过期时间。 对于存入DelayQueue的元素有一定的要求:元素的类型必须是Delayed接口的子类,元素必须重写getDelay(TimeUnit unit)方法用于计算该元素距离过期的剩余时间;如果在消费DelayQueue时发现没有任何一个元素到达过期时间,那么该队列的读取操作会立即返回null,或者使得消费线程进入阻塞。

      SynchronousQueue是一种阻塞队列,每一次对其写入操作必须等待其他线程进行对应的移除操作。SynchronousQueue内部不会涉及到容量、获取size,连peek方法的返回值也永远是null。这种队列主要用于两个线程之间进行数据交换,一个线程专注于数据生产,一个线程专注于数据消费,有别于Exchanger的强调数据交换。

       LinkedBlockingDeque是一个基于链表实现的双向阻塞队列,支持在队头、队尾写入数据和读取移除数据。LinkedBlockingDeque支持可选边界,在初始化时指定。

       LinkedTransferQueue是一个无界队列,具有FIFO的特性。它更像是一个集成了LinkedBlockingQueue和SynchronousQueue特性的阻塞队列,它比SynchronousQueue可以存储更多的元素,在支持LinkedBlockingQueue所有方法的同时又有比它更好的性能表现。LinkedTransferQueue的实现中没有使用锁,同步操作均由CAS算法和LockSupport提供。

3、ConcurrentQueue(并发队列)

     并发队列采用了无锁算法的实现,在该并发场景中性能表现优异。

     ConcurrentLinkedQueue是一种无锁的、线程安全的、性能高效的、基于链表结构实现的FIFO单向队列。

     ConcurrentLinkedDeque是一种无锁的、线程安全的、性能高效的、基于链表结构实现的双向队列。

     并发队列的两种实现都是链表结构,又因为是无锁的,所以在并发环境中调用并发队列的size方法是一种非常低效的方式,得到的数值只是一个近似值。ConcurrentLinkedQueue在执行remove方法删除元素的时候,性能会越来越低,甚至会导致内存泄露问题。这种情况下,可以使用ConcurrentHashSet替代ConcurrentLinkedQueue的解决方案。如果不能使用ConcurrentHashSet做替换,ConcurrentHashSet可以使用poll方法,不过从头部移除元素而无法移除指定元素 ,最终还是需要使用ConcurrentHashSet才可以移除指定元素。

4、ConcurrentMap(并发映射)

    Hashtable或者SynchronizedMap虽然是线程安全的,但是它们的实现方式是使用排他式加锁,在高并发的环境中性能不佳。目前在高并发环境中应用比较多的是ConcurrentHashMap和ConcurrentSkipListMap。

     ConcurrentHashMap是专门为多线程高并发场景设计的Map,它get方法基本上是lock-free的,同时put方法又将锁的粒度控制在很小的范围内,非常适合多线程应用场景中。ConcurrentHashMap使用table数组存储键值对,每个键值对的值是一个链表结构,当链表长度超过8时,链表转换为红黑树。这样,利用CAS+synchronized保证并发更新的安全性,底层结构采用数组+链表+红黑树的存储结构提高查询效率。

      ConcurrentSkipListMap是一种线程安全的并发访问的排序映射表,内部是SkipList结构实现。它在读取性能上低于ConcurrentHashMap,但是它具备两个显著优越性:1、基于跳表的数据结构,ConcurrentSkipListMap的key是有序的;2、可以支持更高的并发,存取数据的时间复杂度与线程数几乎无关。即在数据量一定的情况下,并发的线程越多,ConcurrentSkipListMap的性能优势越明显。

5、写时拷贝算法(Copy On Write)

     CopyOnWrite容器是一种并发容器,简称COW,基本实现思想:在程序运行的初期,所有线程都共享一个数据集合的引用,所有线程对该容器的读取操作将不会对数据集合产生加锁的动作,当有线程对该容器中的数据集合进行删除或增加等写操作时,才会对整个数据集合进行加锁操作,然后将容器中的数据集合复制一份,并且基于最新的复制进行删除或增加等写操作,当写操作执行完毕时,将最新复制的数据集合引用指向原有的数据集合,从而达到读写分离最终一致性的目的。COW容器常被用于读操作远远高于写操作的应用场景中,并且不要求实时数据一致性。

     CopyOnWriteArrayList:用于高并发的ArrayList解决方案,在某种程度上可以代替Collections.synchronizedList。

     CopyOnWriteArraySet:用于高并发的Set解决方案,其实底层是完全基于CopyOnWriteArrayList实现的。

     COW容器的一些缺陷:1、数据复制带来的内存开销;2、只能保证数据的最终一致性,不能保证数据的实时一致性。

6、高并发无锁(Lock-free)数据结构的实现

import java.util.concurrent.atomic.AtomicStampedReference;

public class LockFreeLinkedList<E> {
private AtomicStampedReference<Node<E>> headRef=null;

public LockFreeLinkedList(){
this.headRef=new AtomicStampedReference<>(null,0);
}

public boolean isEmpty() {
return this.headRef.getReference()==null;
}

public void clear() {
this.headRef.set(null, this.headRef.getStamp()+1);
}

public E peekFirst() {
return this.isEmpty()?null:this.headRef.getReference().element;
}

public long count() {
long count=0L;
Node<E> currentNode=this.headRef.getReference();
while(currentNode!=null) {
count++;
currentNode=currentNode.next;
}
return count;
}

public void add(E element) {
if(null==element) {
throw new NullPointerException("The element is null");
}
Node<E> previousNode;
int previousStamped;
Node<E> newNode;
do {
previousNode=this.headRef.getReference();
previousStamped=this.headRef.getStamp();
newNode=new Node<>(element);
newNode.next=previousNode;
}while(!this.headRef.compareAndSet(previousNode, newNode, previousStamped, previousStamped+1));
}

public E removeFirst() {
if(isEmpty()) {
return null;
}else {
Node<E> currentNode;
int currentStamped;
Node<E> nextNode;
do {
currentNode=this.headRef.getReference();
currentStamped=this.headRef.getStamp();
if(currentNode==null) {
break;
}
nextNode=currentNode.next;
}while(!this.headRef.compareAndSet(currentNode, nextNode, currentStamped, currentStamped+1));
return currentNode==null?null:currentNode.element;
}
}

private static class Node<E>{
E element;
volatile Node<E> next;

Node(E element){
this.element=element;
}

public String toString() {
return element==null?"":element.toString();
}
}

}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值