第九章: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容器的时候一定要注意使用场景。   

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • autosize px转dp_Android 屏幕适配以及autoSize的原理.md

    autosize px转dp_Android 屏幕适配以及autoSize的原理.mdAndroidAutoSize的原理px=dp*density;根据百分比适配的话,如果设计稿给的是1080×1920,那么宽就为360dp,像素为1080px,density为3,占满100%如果是在720×1280的话,360dp*density=720,所以这个density为2,将density修改为2就可以了如果是在1080×1920的设计图中,150px,也就是50dp…

    2022年5月7日
    64
  • c++学生管理系统源代码_学校运营管理系统

    c++学生管理系统源代码_学校运营管理系统C#学员管理系统C#学员管理系统是在控制台输出的项目,和OOP学员管理系统相似。①创建一个学员的实体类Student,实现其构造方法和封装:usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;///<summary>///实体类///</sum…

    2022年9月20日
    3
  • 安卓系统框架介绍(安卓常用框架)

    一、什么是Android?Android是由谷歌公司开发的一种基于Linux的开源的操作系统。——源码地址1:https://android.googlesource.com/——源码地址2:http://androidxref.com/起初专为移动设备开发的一个操作系统,如手机,平板电脑,车载系统,智能电视等。Android第一个版本2008年11月发布,至今有超过…

    2022年4月14日
    550
  • Android退出应用程序方法总结[通俗易懂]

    Android退出应用程序方法总结[通俗易懂]Android退出应用程序方法总结在Android开发中,我们运行了应用程序后,都需要退出应用的,那么该如何退出应用,又都有哪些实现方式呢?今天就为大家整理分享一些退出应用程序的方法,一起来看看吧!更新内容Ver:v1任务管理器方法补充 新增监听式退出方法Ver:v2任务管理器方法修正 新增销毁任务栈退出方法1.finish方法finish();该方法只是结束当前Activity,系统将最上面的Activity移出了栈,并没有清理占用的资源。如果栈内有很多Activ

    2022年7月17日
    17
  • jdbctype数据类型_数据类型对应的关键字

    jdbctype数据类型_数据类型对应的关键字mybatis中jdbcType时间类型:当jdbcType=DATE时,传入年月日当jdbcType=TIMESTAMP时,传入年月日+时分秒附录:java.sql.TypesJavaIBMDB2OracleSybaseSQL-SERVER

    2022年9月25日
    2
  • vue的安装和使用_如何正确使用

    vue的安装和使用_如何正确使用前言Vue(读音/vjuː/,类似于view)是一套用于构建前后端分离的框架。刚开始是由国内优秀选手尤雨溪开发出来的,目前是全球“最”流行的前端框架。使用vue开发网页很简单,并且技术生态环境完善

    2022年7月31日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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