2022年Java分布式应用学习笔记JDK的并发包的集合总结 .pdf

上传人:C****o 文档编号:39725255 上传时间:2022-09-07 格式:PDF 页数:8 大小:128.35KB
返回 下载 相关 举报
2022年Java分布式应用学习笔记JDK的并发包的集合总结 .pdf_第1页
第1页 / 共8页
2022年Java分布式应用学习笔记JDK的并发包的集合总结 .pdf_第2页
第2页 / 共8页
点击查看更多>>
资源描述

《2022年Java分布式应用学习笔记JDK的并发包的集合总结 .pdf》由会员分享,可在线阅读,更多相关《2022年Java分布式应用学习笔记JDK的并发包的集合总结 .pdf(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

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=

13、500;intthreadNum;public PutConcurrentHashMap(int threadNum)this.threadNum=threadNum;Overridepublicvoid run()Map concurrentHashMap=newConcurrentHashMap();名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 8 页 -5/8for(int i=0;i elSize;i+)concurrentHashMap.put(i+threadNum,i+threadNum);/*param args*/publicstaticvoid main(S

14、tring args)/启用线程池ExecutorService exec=Executors.newFixedThreadPool(ThreadSIZE);long startTime=System.currentTimeMillis();for(int index=0;index=ThreadSIZE;index+)exec.execute(new PutConcurrentHashMap(index);long endTime=System.currentTimeMillis();exec.shutdown();System.out.println(消耗时间:+(endTime-star

15、tTime)+ms);也是多次执行后,得出一个平均时间吧消耗时间:1869ms时间消耗上差不多哈。在集合元素越来越多的情况下,在解决线程安全的同时保证了时间执行熬费上几乎和非线程安全的Map持平。所以在并发条件下不必自己解决Map的线程安全问题,直接放心使用JDK自己的并发Map包即可,时间性能上还能保证。3.List的并发包可 以 在 高 并 发 环 境 下 使 用java.util.concurrent.CopyOnWriteArrayList代 替java.util.ArrayList。对于添加元素的操作,底层并不像Map那么复杂,就是利用了数组的 copy 功能和加锁机制publicb

16、oolean add(E e)final ReentrantLock lock=this.lock;lock.lock();try Object elements=getArray();int len=elements.length;Object newElements=Arrays.copyOf(elements,len+1);newElementslen=e;setArray(newElements);名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 8 页 -6/8returntrue;finally lock.unlock();它是使用ReentrantLock进行的加锁,

17、之后获得数组进行copy 操作,之后数组个数加一。将新元素填充,之后再对局部变量进行一下set 操作,最后就是解锁操作。至于 remove 操作,和add 的原理一样public E remove(int index)final ReentrantLock lock=this.lock;lock.lock();try Object elements=getArray();int len=elements.length;Object oldValue=elementsindex;int numMoved=len-index-1;if(numMoved=0)setArray(Arrays.copy

18、Of(elements,len-1);else Object newElements=new Objectlen-1;System.arraycopy(elements,0,newElements,0,index);System.arraycopy(elements,index+1,newElements,index,numMoved);setArray(newElements);return(E)oldValue;finally lock.unlock();在枷锁对儿中间,先找到标记下的数组元素,之后创建一个新的临时数组,进行copy,将要删除的对象元素剔除出去!返回被删除元素对象。做 ad

19、d 操作性能与ArrayList进行对比,线程运行400 个,添加元素数是2000 个。对比平均之后发现运行的时间也相差不是很多。并发情况下,CopyOnWriteArrayList比ArrayList略快了那么一点点。get 几乎和 ArrayList没差别,直接从数组中找第index个元素。4.Set 的并发CopyOnWriteArraySet和 CopyOnWriteArrayList底层实现差不多,就是在添加元素的时候需要对对象进行唯一性判断,如果对象数组已经含有重复的元素,不进行增加处理。在此不再赘述。5.Queue的并发队列的并发类是java.util.concurrent.Ar

20、rayBlockingQueue,从类名字上大家估计就能猜 出 来 了,底 层 使 用 的 依 然 是 数 组。这 个ArrayBlockingQueue是继 承自 原始 的名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 8 页 -7/8java.util.AbstractQueue,所以很多方法在父类里面已经有了,只是对于关键方法入队列、出队列操作加入了锁对儿机制。入队列元素操作源码如下publicboolean offer(E e,long timeout,TimeUnit unit)throws InterruptedException if(e=null)thrownew

21、 NullPointerException();long nanos=unit.toNanos(timeout);final ReentrantLock lock=this.lock;lock.lockInterruptibly();try for(;)if(count!=items.length)insert(e);returntrue;if(nanos=0)returnfalse;try nanos=notFull.awaitNanos(nanos);catch(InterruptedException ie)notFull.signal();/propagate to non-inter

22、rupted threadthrow ie;finally lock.unlock();数组未满情况下,执行insert操作的时候,如果数组满了,则进行等待,单位是纳秒。如果超时或者被唤醒了,那么再次判断是否数组已满,如果线程被打断直接抛出异常。出队列方法和入队列差不多,不再赘述。6.AtomicXXXX 的原子类并发包还提供了支持原子操作的Atomic系列类,我们举一个具有代表性的类AtomicInteger类,通常我们使用计数器操作的时候,一般为了避免线程安全的问题,在方法上加锁操作。有了并发包下的原子系列类,我们直接使用即可。关键使用代码片段如下publicstaticint getSu

23、m()returnsum.incrementAndGet();其自增方法底层片段最关键是这么一句if(compareAndSet(current,next)return next;名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 8 页 -8/8此方法具体如下publicfinalboolean compareAndSet(int expect,int update)pareAndSwapInt(this,valueOffset,expect,update);pareAndSwapInt调用了本地native方法直接与底层硬件也就是CPU打交道。大家有兴趣的话可以将sun 的这段代

24、码反编译看看。底层会比较内存上的地址上的值是否为当前值,是就next,不是则反复循环,直到找到当前值。这个和Hibernate的那个乐观锁有异曲同工的意思。其他的一些原子类AtomicBoolean、AtomicLong 等等在此不再赘述,使用以及底层原理都差不多。7.总结与反思关于并发包的集合类就先总结到这里,这次没有将集合的读取元素进行性能对比,实际应用中高并发的读取比集合元素改变(add、remove、replace)更为常见,不过代码很简单,所以就不给出了,有兴趣的朋友认识了这些类后可以自己做实验。至于反思,应该就是这些并发包的资源性能,是否很占用内存空间,加入在一个高并发环境下而且硬件环境又不允许分配给应用系统十分宽容的硬件资源,那么高并发情况下是否玩不转(比如云计算的虚拟化,一台实机可能启动多个虚拟机,作为实机的扩充)。这个问题我们可以使用工具测试jconsole进行监控,也有可能用户的应用自身代码也存在着一系列的问题,还是得具体问题具体分析,总的来说并发包要想实现线程安全,而且时间效率在一般环境下又和非并发包的差不多,需要消耗的内存资源比以前多是一定的,这个是避免不了的,世界是物质的,做任何事情都需要付出代价,只是看这个代价和收益相比值不值得。名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 8 页 -

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 高考资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁