乐趣区

关于java:多线程高并发学习之并发容器

多线程高并发学习之并发容器

HashMap 相干的同步容器

  • 前世今生介绍:HashMap 是从 HashTable 演变过去的,HashTable 设计之初的志愿是容器的的所有办法必须都得是同步的,所以 HashTable 的所有办法都是加了 synchronized 关键字来保障同步,这显然是不太正当的,因为大多数状况下,都是只有一个线程来操作容器,所以又在 HashTable 之后推出了 HashMap

    • HashTable——> 全锁操作
    • HashMap——> 无锁操作
    • ConcurrentHashMap——> 新的同步容器(CAS)
    • HashMap 也应该有同步办法,所以又出了一个 Collections 这么一个工具类,他能够把 HashMap 变成一个同步容器,如下图,还能够将许多容器都变为同步容器
    • Collections.synchronizedMap()给出的同步容器 HashMapHashTable有什么区别呢,区别其实很小,就是 HashTable 用的是办法锁,而Collections.synchronizedMap() 用的是同步代码块 ,都是用的synchronized,只不过 相比于 HashTable 锁的力度要小一些,效率略高一丢丢 ConcurrentHashMap 采纳的是 CAS 无锁操作,在 put 的过程中如果没有发生冲突,则采纳 CAS 操作进行无锁化更新 只有产生了哈希抵触的时候才锁住在链表上增加新 Node 或者更新 Node 的操作
  • 读写效率:

    • HashTable 写入效率高,实测 100W 数据写入用时 700 毫秒左右,读效率低,100W 数据读取用时 37 秒左右
    • Collections.synchronizedMap()写入效率高,实测 100W 数据写入用时 600 毫秒左右,读效率低,100W 数据读取用时 38 秒左右
    • ConcurrentHashMap 写入效率低,实测 100W 数据写入用时 1.8 秒左右,读效率超高,100W 数据读取用时 1.7 秒左右
    • 总结:理论用哪一个须要看我的项目的应用场景,到底是读操作多,还是写操作多,而后依据理论压测数据来决定到底是用哪个同步容器

List、Vector、Queue

  • List 同步须要加锁,或者上图所示,应用 collectios 的 synchronizedXXX 的办法,获取同步的 List,原理也是 synchronized 代码块
  • Vector 是同步容器,是在办法上加了 synchronized 关键字,为办法锁,相比 synchronizedList 锁的力度要大一些,所以效率偏慢一丢丢
  • Queue 队列,实现类中有多个同步队列,都能够实现同步,甚至还有的实现了生产者消费者模型,所以大并发下单个元素的操作,尽量能够多思考 Queue,少思考 List

常常在多线程下应用的容器

Map

  • ConcurrentHashMap,基于 CAS 实现,不多解释
  • ConcurrentSkipListMap,基于跳表实现

    • 可能是因为,思考到利用 CAS 实现了一个 ConcurrentHashMap,也应该须要用 CAS 实现一个”ConcurrentTreeMap“(此处多逼逼一句,TreeMap 基于红黑树实现),但可怜的是,应用 CAS 实现”ConcurrentTreeMap“太难了,难度超高,超简单,所以退一步,应用跳表实现了一个 ConcurrentSkipListMap
  • HashTable
  • Collections.synchronizedMap()

List

  • CopyOnWriteArrayList,写时复制,顾名思义,在写入数据的时候,将 array 数组 copy 一次,如下图

    • 有点相似 Lock 里的 ReadWriteLock,读不加锁,写入加锁
  • Collections.synchronizedList()

    • 和上边提到的 Collections.synchronizedMap()一个样子,都是同步代码块

Queue

  • ConcurrentLinkedQueue

    • 办法介绍:

      • add:增加元素,加不进去,满了,跑异样
      • offer:增加元素,增加胜利返回 true,满了增加不进去了,返回 false
      • peek:获取元素,然而获取后不删除元素
      • poll:获取元素并且删除元素

BlockingQueue:天生的生产者消费者模型

LinkedBlockingQueue 无界队列

  • 介绍:基于链表实现的无界队列,能够始终增加,直到内存溢出
  • 办法介绍:除了上边的 add、offer、peek、poll 办法外

    • put:增加元素,如果增加满了,就阻塞住,期待能够持续增加元素
    • take:获取元素,如果没有能够获取的元素,就阻塞住,期待能够持续往外取元素

ArrayBlockingQueue 有界队列

  • 与 LinkedBlockingQueue 雷同,只不过是指定大小的,有界的

PriorityQueue

  • 介绍:能够依据增加进来的对象进行比拟排序,而后安程序取出

    • 例如:顺次增加“a”、“z”、“f”、“c”,而后循环调用 poll,顺次取出,程序是排好序的

DelayQueue 工夫排序队列

  • 介绍:依据工夫排序,要进入队列的对象必须实现 Delayed 接口,重写 getDelay(获取等待时间)办法以及 compareTo(比拟工夫)办法

SynchronusQueue

  • 介绍:容量为 0 的队列,其实,这货色不是用来装货色的,是用来让一个线程给另外一个线程下达任务的,一个线程往里放数据,期待另一个线程来取数据
  • 容量为 0,所以调用 add 办法往里面增加数据是会报错,正确的应用办法是一个线程调用 put 办法往里放数据,另一个线程调用 take 办法,取数据

TransferQueue

  • 介绍:具备独有的 transfer 办法,调用该办法写入数据,在被其余线程取走数据前始终阻塞的等着,晓得有人将数据取走
  • 办法:

    • transfer 办法,写入数据,并且阻塞住,期待数据被取走后继续执行
说了这么多,那么,你学废了吗?
不点个赞再走嘛

退出移动版