第九章:java 并发容器类了解与使用「建议收藏」

第九章:java 并发容器类了解与使用「建议收藏」第九章:java 并发容器类了解与使用

大家好,又见面了,我是你们的朋友全栈君。

文章转自: https://blog.csdn.net/u012453843/article/details/73824159

并发类容器是专门针对并发设计的,使用ConcurrentHashMap来代替给予散列的传统的HashTable,而且在ConcurrentHashMap中,添加了一些常见复合操作的支持。以及使用了CopyOnWriteArrayList代替Vector,并发的CopyOnWriteArraySet,以及并发的Queue,ConcurrentLinkedQueue和LinkedBlockingQueue,前者是高性能的队列,后者是以阻塞形式的队列,具体实现Queue还有很多,例如ArrayBlockingQueue、PriorityBlockingQueue、SynchronousQueue等。

    下面我们便一一学习下这些并发类容器。


    ConcurrentMap接口有两个重要的实现:ConcurrentHashMap和ConcurrentSkipListMap(支持并发排序功能,弥补ConcurrentHashMap)

    对同步类容器来说比如Vector和HashTable,当有多个线程都要修改容器中的元素的话只能有一个线程进入到容器内进行修改,其它线程都要在外面排队等候,如下图所示。

第九章:java 并发容器类了解与使用「建议收藏」

       ConcurrentHashMap之所以是并发容器是因为它内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。把一个整体分成了16个段(Segment)。也就是最高支持16个线程的并发修改操作。这也是在多线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变量使用volatile关键字声明,目的是第一时间获取修改的内容,性能非常好。
       我们还是以图来说明,在下图中,我们看到ConcurrentHashMap有16个段,你可以把每个段都相当于一个小的HashTable,也就是说每个小段都是加锁的,并发类容器只是把锁的粒度变小了,原来是一把大锁,现在分成了16个小锁。这样可以有效缓解锁竞争的问题,尽量减少因多个线程争抢一把锁而导致的CPU瞬间爆满的情况。现在有5个线程要修改容器中的元素,假如一、二、三、四4个线程访问的是不同的段,那么它们都可以直接访问它们各自段内的数据并且做修改操作,但是如果几个线程访问同一个小段的话(比如线程四和线程五),那么只能进入一个线程进行修改操作,其它线程只能在外等候。也就是说并发类容器虽然支持并发操作,但是如果并发量非常高的话,也是需要耗费很多时间的,毕竟容器内部只有16个分段,一次只能同时处理16个请求,其余的就要排队等候。不过这相比于同步类容器来说,性能已经好太多了。

第九章:java 并发容器类了解与使用「建议收藏」

          下面我们简单看个ConcurrentHashMap的例子吧,可以看到使用chm.putIfAbsent(“k3”, “vvvv”);方法的话,会先判断map中是否已经添加过名为”k3″的key了。如果已经添加过了,那么就不再添加了。如果没有添加过的话,就会添加。

第九章:java 并发容器类了解与使用「建议收藏」

         将chm.putIfAbsent(“k3”, “vvvv”);中的”k3″换成”k4″,这时再执行main方法,如下图所示。

第九章:java 并发容器类了解与使用「建议收藏」

        下面我们来学习下一个并发类容器:Copy-On-Write容器,Copy-On-Write简称COW,是一种用于程序设计中的优化策略。JDK里的COW容器有两种,分别是
CopyOnWriteArrayList和CopyOnWriteArraySet,COW容器非常有用,可以在非常多的场景中使用到。
         那么什么是CopyOnWrite容器呢?CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往容器添加,而是先将当前容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写在不同的容器。
        光说理论不容易理解,还是上图吧,在下图中,当线程一想要对CopyOnWRite容器进行写操作时,CopyOnWrite容器会复制一份容器出来,然后让线程一去修改容器的副本,而不是修改原容器,这时指针依然指在原容器上,写的过程中如果有多个线程想要读取容器中的数据,那么这些线程会去指针指向的容器中去读取数据。不用加任何锁,这样无论有多少个线程同时访问原容器都是可以的,效率当然就很高了。但是肯定有人会有疑问,就是如果同一时间有多个请求过来要修改容器内的数据,会怎么样?这种情况下,当第一个写请求被容器收到后,容器复制出一个副本出来,线程一去这个副本修改数据,其它线程也要修改容器数据,那就只能在副本容器外排队等候,因为容器的写操作是加锁的。

第九章:java 并发容器类了解与使用「建议收藏」

         当写操作完成后,指针会指向容器的副本,原容器将会被销毁,被垃圾回收器回收。后续再有线程来请求读取容器中的数据的话,就会读取副本这个容器中的数据了。

第九章:java 并发容器类了解与使用「建议收藏」

          下面我们来看下CopyOnWriteArrayList和CopyOnWriteArraySet的一个小例子,如下图所示,可以看到CopyOnWriteArrayList与ArrayList的用法是一样的,
CopyOnWriteArraySet与Set的用法也是一样的。

第九章:java 并发容器类了解与使用「建议收藏」

         代码如下:

[html] 
view plain
 copy

  1. package com.internet.container;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.concurrent.CopyOnWriteArrayList;  
  5. import java.util.concurrent.CopyOnWriteArraySet;  
  6.   
  7. public class UseCopyOnWrite {  
  8.      
  9.     public static void main(String[] args) {  
  10.         //ArrayList怎样使用,CopyOnWriteArrayList就怎样使用  
  11.         CopyOnWriteArrayList<String> cwal = new CopyOnWriteArrayList<>();  
  12.         cwal.add(“a”);  
  13.         cwal.add(“b”);  
  14.         for(String str : cwal){  
  15.             System.out.println(“str:”+str);  
  16.         }  
  17.         //Set怎样使用,CopyOnWriteArraySet就怎样使用  
  18.         CopyOnWriteArraySet<String> cwas = new CopyOnWriteArraySet<>();  
  19.         cwas.add(“c”);  
  20.         cwas.add(“d”);  
  21.         for (Iterator iterator = cwas.iterator(); iterator.hasNext();) {  
  22.             String string = (String) iterator.next();  
  23.             System.out.println(“string:”+string);  
  24.         }  
  25.     }  
  26. }  

        两种容器add方法都添加了重入锁,如下图所示

第九章:java 并发容器类了解与使用「建议收藏」

          CopyOnWriteArraySet.class类中的代码如下:

第九章:java 并发容器类了解与使用「建议收藏」

         那么CopyOnWrite容器的应用场景是什么呢?
        CopyOnWrite容器特别适用于读多写少的场景,就是基本上都是在读,很少有写的请求的场景。如果是写多读少的话,就不适合,因为CopyOnWrite处理写请求是要复制一份副本出来的,如果容器内的数据量非常大的话,频繁的复制副本将会非常消耗性能,这种场景下CopyOnWrite容器就根本不适用,它甚至没有你在写操作的地方加一把synchronized锁的性能高。所以我们应用CopyOnWrite容器的时候一定要注意使用场景。   

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106138.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • rfid-rc522使用教程_RFID读写方式是什么

    rfid-rc522使用教程_RFID读写方式是什么文章目录1、RC522驱动原理2、手机APP查看卡信息3、驱动移植4、读写卡1、RC522驱动原理2、手机APP查看卡信息这里我参考的这篇博主的文章,讲的还是蛮详细的:https://blog.csdn.net/Ikaros_521/article/details/115958888使用的APP如下所示:可以按照软件操作读取一个卡的信息如下所示,可以看到他是16个扇区,每个扇区有四个块,所以就一共是64块,储存的信息数量是1024byte,计算方式为16416=1024。下面的几页信息如

  • Python 判断文件是否存在的三种方法

    Python 判断文件是否存在的三种方法这里将介绍三种判断文件或文件夹是否存在的方法,分别使用os模块、Try语句、pathlib模块。1.使用os模块os模块中的os.path.exists()方法用于检验文件是否存在。判断文件是否存在import osos.path.exists(test_file.txt)#True

  • Android SDK下载配置教程

    Android SDK下载配置教程https://blog.csdn.net/naipeng/article/details/72722682/ 下面详细介绍。第一步、安装JDKAndroid开发工具要求必须安装JDK(JavaDevelopmentKit),不能只安装JRE(Java RuntimeEdition),在安装Android开发工具之前需要先安装JavaJDK。尤其是Eclipse的开发过程必须要…

  • visifire 控件

    visifire 控件引言Silverlight对于图形图像处理方面,从1.0时代起就给予了很强大的支持,所以我们可以在Silverlight中实现非常棒的各种统计图表,然而现在有了一些开源的项目,使得这项工作更加的简单。本文我将介绍一个开源的项目visifire,使用它可以在Silverlight2中实现超酷的图表。简单图表首先我们需要下载Visifire项目Silverlight开发包,在建立完项…

  • strip 命令的使用方法

    strip 命令的使用方法

  • eclipse安装教程详细教程_eclipse中文版教程详细教程

    eclipse安装教程详细教程_eclipse中文版教程详细教程参考与: https://www.cnblogs.com/ForestDeer/p/6647402.html第一步:下载eclipse,并安装。下载链接:http://www.eclip

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号