java并发程序设计总结七:jdk的并发容器
来源:互联网 发布:宜兴俊知集团工资高吗 编辑:程序博客网 时间:2024/05/16 07:55
线程安全的HashMap:Collections.synchronizedMap()
Collections工具类中提供了一系列synchronizedXxx方法用于包装对应的集合对象成线程安全的。这里就介绍下HashMap:
public static <K,V>Map<K,V> synchronizedMap(Map<K,V> m);
该方法会生成一个名为synchronizedMap的Map,它使用了委托机制,将自己所有的Map相关的功能交给传入的Map实现,自己主要负责保证线程安全。synchronizedMap类的参考实现部分如下:
private static class synchronizedMap<K,V> implements Map<K,V>,Serializable{ private static final long serialVersionUID = ...L; private final Map<K,V> m; final Object mutex;}
synchronizedMap同步实现的核心是通过mutex这个对象,在synchronizedMap中所有的map相关功能操作都是通过同步mutex对象实现的,比如对于map.get方法,他的实现如下:
public V get(Object obj){ synchronized(mutex){ .. }}
该map可以满足线程安全的要求,但是,他在多线程环境中的性能非常低:无论是对map的读取还是写入,都需要获得mutex锁,这回导致所有对map的操作都会进入等待状态,直到mutex锁可用。在多线程高并发的情况下我们需要寻求新的解决方案
线程安全的HashMap:concurrentHashMap
concurrentHashMap内部进一步细分了若干个小的HashMap,称之为段,一个concurrentHashMap内部默认情况下被细分为16个段(通过减少锁细粒度的方式来削弱多线程的锁竞争)
当concurrentHashMap中增加一个新的表项时,并不是将整个HashMap加锁,而是会根据hashcode得到该表项存放在哪一个段中,然后对该段进行加锁,并完成put操作。
使用concurrentHashMap获取size等全局信息的方法调用时,在其内部会获得每个段的相应数据,最后进行统计,这样就需要获得每个段的锁,最终的性能大大低于同步的HashMap
高效读写的队列:ConcurrentLikedQueue
jdk中提供了一个ConcurrentLinkedQueue用来实现高并发的队列,从名字可以看出,这个队列使用链表作为其数据结构。作为一个链表,自然需要定义有关链表的节点,在ConcurrentLinkedQueue中,定义的节点Node核心如下:
private static class Node<E>{ volatile E item; volatile Node<E> next;}
在ConcurrentLinkedQueue内部有两个重要的字段,head/tail,分别表示链表的头部和尾部,他们都是Node类型。其中tail尾节点最为特殊,一般来说,我们都是期望每次tail都是指向链表的末尾,但实际上,tail的更新不是及时的,每次更新会滞后跳跃一个元素。
高效读取:CopyOnWriteArrayList
之前博客说到ReadWriteLock读写分离锁,其内部分别为读和写定义了两个锁:writeLock和readLock,他实现了读读和读写相互之间不会发生等待的事件;这里使用CopyOnWriteArrayList同样实现了读读和读写之间不会进行同步等待,不过他的内部实现不是通过为读和写分别设置两个锁,它的实现是通过在进行写入操作时,进行一次自我复制,写完之后,再将修改完后的副本替换原来的数据,从而实现了写操作不会影响读了
有关读取的实现细节如下:
private volatile transient Object[] Array;public E get(int index){ return get(getArray(),index);}final Object[] getArray(){ return Array;}private E get(Object[] a,int index){ return (E) a[index];}
相比读取,其写入的实现就复杂一点:
public boolean add(E e){ final ReentrantLock lock = this.lock; lock.lock(); try{ Object[] elements = getArray(); int len = elements.length; Object[] newElements = Array.copyOf(elements,len+1); newElements[len] = e; setArray(newElements); return true; } catch(Exception e){ ... } finally{ lock.unlock(); }}
从实现代码中可以看到,其复制了elements为一个新的newElements,然后对新的进行操作,最后通过set赋值,从而实现修改
随机数据结构:跳表
jdk并发包中,除了常用的哈希表外,还实现了一种有趣的数据结构–跳表,跳表是一种可以用来快速查找的数据结构。跳表使用的是随机算法,它的本质是同时维护了多个链表,并且链表是分层的,最底层的链表包含了所有的元素,自底向上每一层链表都是下面一层链表的子集,一个元素插入到哪些层是完全随机的。
虽然跳表中的元素位置都是随机的,使用随机算法来分配,但是跳表中的所有链表的元素都是有序的,在跳表中查找一个元素,首先从顶层的链表中查找,然后一层一层往下找,在跳表中的查询操作的时间复杂度是O(logn)。
在并发数据结构中,jdk使用跳表来实现一个map
实现这一数据结构的类是ConcurrentSkipListMap,它继承了AbstractMap,对跳表的操作和HashMap类似,不同的在于,对跳表的遍历输出是有序的
参考文献
java高并发程序设计第三章
阅读全文
0 0
- java并发程序设计总结七:jdk的并发容器
- 《Java高并发程序设计》学习 --3.3 JDK的并发容器
- 《Java高并发程序设计》总结--3. JDK并发包
- java并发包:jdk并发容器
- java高并发程序设计总结三:JDK并发包之ReentrantLock重入锁
- java高并发程序设计总结四:JDK并发包之信号量Semaphore
- Java高并发程序设计笔记(七)锁的优化
- Java高并发程序设计总结
- java高并发程序设计学习笔记七并发设计模式
- JDK并发容器
- java并发包学习系列:jdk并发容器
- Java高并发程序设计 JDK并发包(上)
- 实战Java高并发程序设计(三)JDK并发包
- java高并发程序设计总结五:jdk并发包其他同步控制工具类:ReadWriteLock/CountDownLatch/CyclicBarrier/LockSupport
- JDK容器与并发—并发
- Java的同步容器和并发容器
- Java并发编程 并发容器
- Java并发容器并发集合
- seglink代码之 generate_anchors_one layer
- TURN协议(RFC5766详解)
- 关于媒体报道“水滴平台分享课堂画面”新闻的说明
- 任正非:华为总部不会离开深圳
- 五一小长假来袭 vivo Xplay6助你玩转假期
- java并发程序设计总结七:jdk的并发容器
- 蚂蚁金服要打造智慧社区标准解决方案,这背后有什么门道?
- 李开复:人工智能时代的科学家创业 | GMIC 2017
- 500 Startups创始人Dave McClure:我对完美的创业团队没兴趣 | GMIC 2017
- HTML的子代选择器
- [console] early printk实现流程
- 百度Q1 AI拉动营收增长6.8% 盘后股价下挫逾5%
- 深圳消委回应华为P10闪存门:出厂后随机发货,没有区别对待消费者
- 臻迪水下机器人PowerRay小海鳐面向全国开售 三个版本诚意定价