经常使用并发类

http://www.javashuo.com/article/p-zgvbluax-ev.html数组

 一、经常使用的五种并发包安全

  • ConcurrentHashMap
  • CopyOnWriteArrayList
  • CopyOnWriteArraySet
  • ArrayBlockingQueue
  • LinkedBlockingQueue

二、ConcurrentHashMap数据结构

  • 线程安全的HashMap的实现
  • 数据结构:一个指定个数的Segment数组,数组中的每个元素Segment至关于一个HashTable(一个HashEntry[])
  • 扩容的话,只须要扩本身的Segment而非整个table扩容
  • key与value均不能够为null,而hashMap能够
  • 向map添加元素
    • 根据key获取key.hashCode的hash值
    • 根据hash值算出将要插入的Segment
    • 根据hash值与Segment中的HashEntry的容量-1按位与获取将要插入的HashEntry的index
    • 若HashEntry[index]中的HashEntry链表有与插入元素相同的key和hash值,根据onlyIfAbsent决定是否替换旧值
    • 若没有相同的key和hash,直接返回将新节点插入链头,原来的头节点设为新节点的next(采用的方式与HashMap一致,都是HashEntry替换的方法)
  • ConcurrentHashMap基于concurrencyLevel划分出多个Segment来存储key-value,这样的话put的时候只锁住当前的Segment,能够避免put的时候锁住整个map,从而减小了并发时的阻塞现象
  • 从map中获取元素
    • 根据key获取key.hashCode的hash值
    • 根据hash值与找到相应的Segment
    • 根据hash值与Segment中的HashEntry的容量-1按位与获取HashEntry的index
    • 遍历整个HashEntry[index]链表,找出hash和key与给定参数相等的HashEntry,例如e
      • 如没找到e,返回null
      • 如找到e,获取e.value
        • 若是e.value!=null,直接返回
        • 若是e.value==null,则先加锁,等并发的put操做将value设置成功后,再返回value值
  • 对于get操做而言,基本没有锁,只有当找到了e且e.value等于null,有多是当下的这个HashEntry刚刚被建立,value属性尚未设置成功,这时候咱们读到是该HashEntry的value的默认值null,因此这里加锁,等待put结束后,返回value值
  • 加锁状况(分段锁):
    • put
    • get中找到了hash与key都与指定参数相同的HashEntry,可是value==null的状况
    • remove
    • size():三次尝试后,还未成功,遍历全部Segment,分别加锁(即创建全局锁)

 

三、CopyOnWriteArrayList并发

  • 线程安全且在读操做时无锁的ArrayList
  • 采用的模式就是"CopyOnWrite"(即写操做-->包括增长、删除,使用复制完成)
  • 底层数据结构是一个Object[],初始容量为0,以后每增长一个元素,容量+1,数组复制一遍
  • 遍历的只是全局数组的一个副本,即便全局数组发生了增删改变化,副本也不会变化,因此不会发生并发异常。可是,可能在遍历的过程当中读到一些刚刚被删除的对象
  • 增删改上锁、读不上锁
  • 读多写少且脏数据影响不大的并发状况下,选择CopyOnWriteArrayList

四、CopyOnWriteArraySet高并发

  • 基于CopyOnWriteArrayList,不添加剧复元素

五、ArrayBlockingQueue性能

  • 基于数组、先进先出、线程安全,可实现指定时间的阻塞读写,而且容量能够限制
  • 组成:一个对象数组+1把锁ReentrantLock+2个条件Condition
  • 三种入队对比
    • offer(E e):若是队列没满,当即返回true; 若是队列满了,当即返回false-->不阻塞
    • put(E e):若是队列满了,一直阻塞,直到数组不满了或者线程被中断-->阻塞
    • offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,若是数组已满,则进入等待,直到出现如下三种状况:-->阻塞
      • 被唤醒
      • 等待时间超时
      • 当前线程被中断
  • 三种出对对比
    • poll():若是没有元素,直接返回null;若是有元素,出队
    • take():若是队列空了,一直阻塞,直到数组不为空或者线程被中断-->阻塞
    • poll(long timeout, TimeUnit unit):若是数组不空,出队;若是数组已空且已经超时,返回null;若是数组已空且时间未超时,则进入等待,直到出现如下三种状况:
      • 被唤醒
      • 等待时间超时
      • 当前线程被中断
  • 须要注意的是,数组是一个必须指定长度的数组,在整个过程当中,数组的长度不变,队头随着出入队操做一直循环后移
  • 锁的形式有公平与非公平两种
  • 在只有入队高并发或出队高并发的状况下,由于操做数组,且不须要扩容,性能很高

六、LinkedBlockingQueuespa

  • 基于链表实现,读写各用一把锁,在高并发读写操做都多的状况下,性能优于ArrayBlockingQueue
  • 组成一个链表+两把锁+两个条件
  • 默认容量为整数最大值,能够看作没有容量限制
  • 三种入队与三种出队与上边彻底同样,只是因为LinkedBlockingQueue的的容量无限,在入队过程当中,没有阻塞等待
相关文章
相关标签/搜索