高并发下的Java数据结构ListSetMapQueue/

 

一.复习一下普通集合框架

LinkedList

LinkedList同时实现了List接口和Deque接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack) 。当你需要使用栈或者队列时,可以考虑使用LinkedList,一方面是因为Java官方已经声明不建议使用Stack类,更遗憾的是,Java里根本没有一个叫做Queue的类(它是个接口名字)。

ArrayDeque

关于栈或队列,现在的首选是ArrayDeque,它有着比LinkedList(当作栈或队列使用时)有着更好的性能。

PriorityQueue

前面以Java ArrayDeque为例讲解了StackQueue,其实还有一种特殊的队列叫做PriorityQueue,即优先队列。优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素)。这里牵涉到了大小关系,元素大小的评判可以通过元素本身的自然顺序(natural ordering),也可以通过构造时传入的比较器(Comparator,类似于C++的仿函数)。

Java中PriorityQueue实现了Queue接口,不允许放入null元素;其通过堆实现,具体说是通过完全二叉树(complete binary tree)实现的小顶堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值),也就意味着可以通过数组来作为PriorityQueue的底层实现。

 WeakHashMap 的工作原理,还需要引入一个概念 : 弱引用(WeakReference)。我们都知道Java中内存是通过GC自动管理的,GC会在程序运行过程中自动判断哪些对象是可以被回收的,并在合适的时机进行内存释放。GC判断某个对象是否可被回收的依据是,是否有有效的引用指向该对象。如果没有有效引用指向该对象(基本意味着不存在访问该对象的方式),那么该对象就是可回收的。这里的有效引用 并不包括弱引用。也就是说,虽然弱引用可以用来访问对象,但进行垃圾回收时弱引用并不会被考虑在内,仅有弱引用指向的对象仍然会被GC回收。引用类似参考Java引用类型原理

 二、高并发下的Java数据结构ListSetMapQueue

1. 并发list总结 

获取线程安全的List我们可以通过Vector、Collections.synchronizedList()方法和CopyOnWriteArrayList三种方式
读多写少的情况下,推荐使用CopyOnWriteArrayList方式
读少写多的情况下,推荐使用Collections.synchronizedList()的方式

原文链接:https://blog.csdn.net/weixin_45668482/article/details/117396603

2. 并发set 和并发list类似

3.并发map

HashTable

Collections.synchronizedMap(性能和HashTable差不多)

ConcurrentHashMap(并发性能最好)

  •    jdk1.7之前是用分段所,分段数量初始化后就无法更改,锁的粒度固定了;
  •    1.8之后使用Node+CAS+ synchronized + volatile。底层数据结构数组+链表+红黑树。数组可以扩容,数组中的每个元素看成一个桶。可以看到大部分都是CAS操作,加锁的部分是对桶的头节点进行加锁,锁粒度很小。
  •  通过源码可以看出 使用 CAS + synchronized 方式时 加锁的对象是每个链条的头结点,也就是 锁定 的是冲突的链表,所以再次提高了并发度,并发度等于链表的条数或者说 桶的数量。那为什么sement 不把段的大小设置为一个桶呢,因为在粒度比较小的情况下,如果使用ReentrantLock则需要节点继承AQS来获得同步支持,增加内存开销,而1.8中只有头节点需要进行同步,粒度表较小,相对来说内存开销就比较大。所以不把segment的大小设置为一个桶。

 Java高并发之ConcurrentHashMap(锁分段技术、结构、初始化、如何定位、常见操作、JDK1.8中的变化)

java8的ConcurrentHashMap为何放弃分段锁,为什么要使用CAS+Synchronized取代Segment+ReentrantLock

4.并发Queue

线程安全LinkedBlockingQueue(ReentrantLock),ConcurrentLinkedQueue (并发性能最好,全程无锁)

  1. ConcurrentLinkedQueue 无界是因为结构是用链表组成的,天生无界,当然受到系统资源大小限制;

  2. ConcurrentLinkedQueue 在入队和出队时,均采用了减少 CAS 更新 head 和 tail 的操作,提升了性能;

  3. ConcurrentLinkedQueue 采用非阻塞模式实现,即无锁,通过自旋和 CAS 实现线程安全;

死磕 Java 并发编程(9):无界线程安全队列 ConcurrentLinkedQueue 源码解析

BlockingQueue 是阻塞队列,而在于简化多线程间的数据共享。典型应用场景:生产者消费者模型

其他队列有:

数组阻塞队列 ArrayBlockingQueue

延迟队列 DelayQueue

链阻塞队列 LinkedBlockingQueue(线程安全,性能比ConcurrentLinkedQueue差)

具有优先级的阻塞队列 PriorityBlockingQueue (可以实现排序)

同步队列 SynchronousQueue (它的内部同时只能够容纳单个元素。如果该队列已有一元素的话,试图向队列中插入一个新元素的线程将会阻塞,直到另一个线程将该元素从队列中抽走。同样,如果该队列为空,试图向队列中抽取一个元素的线程将会阻塞,直到另一个线程向队列中插入了一条新的元素)

5.并发Deque

Deque 接口的实现类:LinkedList (不安全)、ArrayDeque (不安全)和LinkedBlockingDeque(线程安全)。

BlockingDeque 类是一个双端队列。在线程既是一个队列的生产者又是这个队列的消费者的时候可以使用到 BlockingDeque。如果生产者线程需要在队列的两端都可以插入数据,消费者线程需要在队列的两端都可以移除数据,这个时候也可以使用 BlockingDeque

 

posted on 2023-10-28 16:31  yuluoxingkong  阅读(78)  评论(0编辑  收藏  举报