2022年Java分布式应用学习笔记JDK的并发包的集合总结 .pdf
《2022年Java分布式应用学习笔记JDK的并发包的集合总结 .pdf》由会员分享,可在线阅读,更多相关《2022年Java分布式应用学习笔记JDK的并发包的集合总结 .pdf(8页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、1/8Java 分布式应用学习笔记04JDK的并发包的集合总结刘岩Email: 1.前言平时咱们使用的HashMap、ArrayList等等容器集合包都存在线程安全的问题,看过JDK源码的各位朋友们知道这些实现类底层,为了性能,都没有对这些集合的操作方法做加锁或者副本传递机制,只有Vector 和 Stack 是线程安全的,大家可以看它们的源码,底层方法是以在方法上加上synchronized作为代价的,换句话说是用时间换取空间的方式。Sun JDK 对 多 线 程 并 发 环 境 下 做 了 很 多 并 发 的 解 决 方 案,其 类 大 都 在java.util.concurrent.*下
2、面,此包下的类和java.util.*包下面的集合类,在使用上几乎没什么太大分别,想想也是啊!他们都是实现接口规范:List、Set、Map的。只要接口规范不变,那么在使用上也不应该有何变化,实现机制是一个侧重低概率并发或者就是单线程环境下,并发包则侧重高并发情况的系统。大家可以看看Tomcat 的源代码,其中org.apache.catalina.core.ApplicationContext里 面 就使 用 到了 并 发包,因 为Tomcat 作为 Web容器一定要接受来自各个客户端的request,进而分配Web应用上下文信息,应用参数key-value值等等。又得满足并发的请求、又得满
3、足性能所需,所以它使用 JDK 的并发包。在使用层面上,笔者并不作过多介绍,可以参考非并发包的使用。至于这些非并发包的底层实现方式可以参考笔者的blog 关于 Java 基础数据结构的基础知识,而是介绍一下这些并发包的底层机制和性能对比,在多线程环境下,用并发包和不用并发包的时间效率对比,空间资源效率不用比了,肯定单线程那些包消耗的比多线程消耗的小得多,毕竟做任何事都是要付出代价的。http:/ 接口,而ConcurrentMap 接口又是继承自Map接口的扩展。先看看它是如何实现put 操作的。public V put(K key,V value)if(value=null)thrownew
4、 NullPointerException();int hash=hash(key.hashCode();return segmentFor(hash).put(key,hash,value,false);首先判断值是否为空,空值不必要存储,之后根据key 的哈希值计算一个hash 值。根据计算出的hash 值去获取segment 对象。找到了 segment 对象后调用该对象的put 方法完成操作。Segment 是 ConcurrentHashMap的内部类其底层原理使用一个transient volatile HashEntry table;进行存取。现在再看segment 内的 put
5、 源码 V put(K key,int hash,V value,boolean onlyIfAbsent)lock();try int c=count;if(c+threshold)/ensure capacity名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 8 页 -2/8 rehash();HashEntry tab=table;int index=hash&(tab.length-1);HashEntry first=tabindex;HashEntry e=first;while(e!=null&(e.hash!=hash|!key.equals(e.key)e=e.
6、next;V oldValue;if(e!=null)oldValue=e.value;if(!onlyIfAbsent)e.value=value;else oldValue=null;+modCount;tabindex=new HashEntry(key,hash,first,value);count=c;/write-volatile return oldValue;finally unlock();首先是进行加锁操作,之后就是进行数组大小的判断,如果容量不够,则需要扩充。之后再通过对hash 值的按位与的操作后,得到了这个key 所要放置的位置。有了位置了,再看 HashEntry
7、数组组成的对象链,是否已经有key,如果有了,覆盖 value 操作,如果没有,创建一个新的HashEntry 对象,重新组成HashEntry 链表,最后进行解锁操作。所以直线我们关心的在put 中会出现的线程安全问题,看了源码后是不是就解决了。想想除了 put 操作会出现线程不安全的隐患外,我们来看看remove 操作。删除操作代码原理与put 操作类似,也是通过hash 值找到那个segment 对象,之后调用segment 的 remove方法去完成真正的操作。真正的操作也是先加锁,之后迭代 HashEntry,直到找到了传入的hash 值相同的。找到了删之,重新建立链表!找不到,ov
8、er,然后释放对象锁。在 ConcurrentHashMap 的 get、containsKey等等这种不破坏原子性的读取(read)操作可以说大部分情况下没有进行加锁操作,即便像get 加了锁操作,也是极其轻量的,仅仅是加锁了一行很简单的读取操作代码,如下 V readValueUnderLock(HashEntry e)lock();try return e.value;名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 8 页 -3/8 finally unlock();下面我们来看看性能,在此所说的性能仅仅指时间执行效率。使用一般HashMap包的程序如下package th
9、readConcurrent.hashMap;import java.util.HashMap;import java.util.Map;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*author liuyan*/publicclass PubHashMap implements Runnable finalstaticintThreadSIZE=2500;finalstaticintelSize=500;intthreadNum;public PubHashMap(int
10、 threadNum)this.threadNum=threadNum;Overridepublicvoid run()Map hashMap=new HashMap();for(int i=0;i elSize;i+)hashMap.put(i+threadNum,i+threadNum);/*param args*/publicstaticvoid main(String args)/启用线程池ExecutorService exec=名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 8 页 -4/8Executors.newFixedThreadPool(ThreadSIZE
11、);long startTime=System.currentTimeMillis();for(int index=0;index=ThreadSIZE;index+)exec.execute(new PubHashMap(index);long endTime=System.currentTimeMillis();exec.shutdown();System.out.println(消耗时间:+(endTime-startTime)+ms);启动 2500 个线程,每个线程往HashMap中添加 500 个字符串元素。执行多次后给出一个平均时间吧消耗时间:1753ms使用并发包程序如下pac
12、kage threadConcurrent.hashMap;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*author liuyan*/publicclass PutConcurrentHashMap implements Runnable finalstaticintThreadSIZE=2500;finalstaticintelSize=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年Java分布式应用学习笔记JDK的并发包的集合总结 2022 Java 分布式 应用 学习 笔记 JDK 并发 集合 总结
限制150内