Java数据结构

数据结构

1 队列

  • 非阻塞队列:ConcurrentLinkedQueue(无界线程安全),采用CAS机制(compareAndSwapObject原子操作)。
  • 阻塞队列:ArrayBlockingQueue(有界)、LinkedBlockingQueue(无界)、DelayQueue、PriorityBlockingQueue,采用锁机制;使用 ReentrantLock 锁。

Set/HashSet

注重独一无二的性质,该体系集合可以知道某物是否已近存在于集合中,不会存储重复的元素,用于存储无序且值不能重复的对象。

TreeSet

  • 也就是元素需要实现Comparable接口,覆盖compareTo 方法。这种方式也作为元素的自然排序,也可称为默认排序。
  • 让容器自身具备比较性,自定义比较器

LinkedHashSet

会保存插入的顺序

List/ArraryList

ArrayList是一个数组实现的列表,由于数据是存入数组中的,所以它的特点也和数组一样,查询很快,但是中间部分的插入和删除很慢。

Vector

Vector就是ArrayList的线程安全版,它的方法前都加了synchronized锁,其他实现逻辑都相同。
如果对线程安全要求不高的话,可以选择ArrayList,毕竟synchronized也很耗性能。

LinkedList

故名思意就是链表,LinkedList还是一个双向链表。LinkedList继承于AbstractSequentialList,和ArrayList一个套路。内部维护了3个成员变量,一个是当前链表的头节点,一个是尾部节点,还有是链表长度。

ArrayList是由数组实现的,方便查找,返回数组下标对应的值即可,适用于多查找的场景
LinkedList由链表实现,插入和删除方便,适用于多次数据替换的场景

HashMap

HashMap没有对多线程的场景下做任何的处理,不用说别的,就两个线程同时put,然后冲突了,两者需要操作一个链表/红黑树,这肯定就会有错误发生,所以HashMap是线程不安全的。

HashTable

HashTable线程安全,但是它的线程安全是依赖将所有修改HashTable的代码块都用synchronized修饰。在HashTable中,一个线程在put,其余的线程在get的时候就会被阻塞,无法并行。所以不推荐使用HashTable,虽然它线程安全。

ConcurrentHashMap

jdk1.7之前ConcurrentHashMap 采用了分段锁技术,其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理,理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。

jdk1.8之后抛弃了原有的 Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性。也将 1.7 中存放数据的 HashEntry 改为 Node,但作用都是相同的。其中的 val next 都用了 volatile 修饰,保证了可见性。

ConcurrentSkipListMap

ConcurrentSkipListMap 的key是有序的。ConcurrentSkipListMap 支持更高的并发。ConcurrentSkipListMap 的存取时间是log(N),和线程数几乎无关。也就是说在数据量一定的情况下,并发的线程越多,ConcurrentSkipListMap越能体现出他的优势。注意,调用ConcurrentSkipListMap的size时,由于多个线程可以同时对映射表进行操作,所以映射表需要遍历整个链表才能返回元素个数,这个操作是个O(log(n))的操作。

ConcurrentLinkedQueue

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用了“wait-free”算法来实现,该算法在Michael & Scott算法上进行了一些修改。

解决并发时的安全问题也有两种策略:
CAS

实际上虚拟机采用CAS配合上失败重试的方式保证更新操作的原子性,原理和上面讲的一样。CAS,全称为Compare and Swap,即比较-替换。假设有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回true,否则什么都不做并返回false。当然CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,永远是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功。

TLAB

如果使用CAS其实对性能还是会有影响的,所以JVM又提出了一种更高级的优化策略:每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲区(TLAB),线程内部需要分配内存时直接在TLAB上分配就行,避免了线程冲突。只有当缓冲区的内存用光需要重新分配内存的时候才会进行CAS操作分配更大的内存空间。
虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来进行配置(jdk5及以后的版本默认是启用TLAB的)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值