ConcurrentHashMap

ConcurrentHashMap底层具体实现

JDK 1.7底层实现

将数据分为一段一段的存储,然后给每一段数据配一把锁, 当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。

ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。 其中Segment 实现了 ReentrantLock,所以Segment是一种可重入锁,扮演锁的角色。 HashEntry 用于存储键值对数据。

一个ConcurrentHashMap里包含一个Segment数组。 Segment结构和HashMap类似,是一种数组和链表结构, 一个Segment包含一个HashEntry数组,每个HashEntry是一个链表结构的元素, 每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得对应的Segment的锁。

JDK 1.8底层实现

TreeBin: 红黑二叉树节点 Node: 链表节点

ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。 数据结构与HashMap1.8的结构类似,数组+链表/红黑二叉树(链表长度>8时,转换为红黑树)。

synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash值不冲突,就不会产生并发。

ConcurrentHashMap和Hashtable的区别

底层数据结构:

  • JDK1.7 的ConcurrentHashMap底层采用分段的数组+链表实现, JDK1.8 的ConcurrentHashMap底层采用的数据结构与JDK1.8 的HashMap的结构一样,数组+链表/红黑二叉树。

  • Hashtable和JDK1.8 之前的HashMap的底层数据结构类似都是采用数组+链表的形式, 数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的

实现线程安全的方式

  • JDK1.7的ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment), 每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 JDK 1.8 采用数组+链表/红黑二叉树的数据结构来实现,并发控制使用synchronized和CAS来操作。

  • Hashtable:使用 synchronized 来保证线程安全,效率非常低下。 当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态, 如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈。

HashTable全表锁

ConcurrentHashMap分段锁

CopyOnWriteArrayList

public class CopyOnWriteArrayList<E> extends Object
implements List<E>, RandomAccess, Cloneable, Serializable复制代码

在很多应用场景中,读操作可能会远远大于写操作。 由于读操作根本不会修改原有的数据,因此对于每次读取都进行加锁其实是一种资源浪费。 我们应该允许多个线程同时访问List的内部数据,毕竟读取操作是安全的。 这和ReentrantReadWriteLock读写锁的思想非常类似,也就是读读共享、写写互斥、读写互斥、写读互斥。 JDK中提供了CopyOnWriteArrayList类比相比于在读写锁的思想又更进一步。 为了将读取的性能发挥到极致,CopyOnWriteArrayList 读取是完全不用加锁的,并且写入也不会阻塞读取操作。 只有写入和写入之间需要进行同步等待。这样,读操作的性能就会大幅度提高。

CopyOnWriteArrayList的实现机制

CopyOnWriteArrayLis 类的所有可变操作(add,set等等)都是通过创建底层数组的新副本来实现的。 当 List 需要被修改的时候,我并不修改原有内容,而是对原有数据进行一次复制,将修改的内容写入副本。 写完之后,再将修改完的副本替换原来的数据,这样就可以保证写操作不会影响读操作了。

CopyOnWriteArrayList是满足CopyOnWrite的ArrayList, 所谓CopyOnWrite也就是说: 在计算机,如果你想要对一块内存进行修改时,我们不在原有内存块中进行写操作, 而是将内存拷贝一份,在新的内存中进行写操作,写完之后呢,就将指向原来内存指针指向新的内存, 原来的内存就可以被回收掉了。

CopyOnWriteArrayList读取和写入源码简单分析

  • CopyOnWriteArrayList读取操作的实现

读取操作没有任何同步控制和锁操作, 因为内部数组array不会发生修改,只会被另外一个array替换,因此可以保证数据安全。

/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
public E get(int index) {return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {return (E) a[index];
}
final Object[] getArray() {return array;
}复制代码

  • CopyOnWriteArrayList读取操作的实现

CopyOnWriteArrayList 写入操作 add() 方法在添加集合的时候加了锁, 保证同步,避免了多线程写的时候会复制出多个副本出来。

/*** Appends the specified element to the end of this list.*/
public boolean 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);//拷贝新数组newElements[len] = e;setArray(newElements);return true;} finally {lock.unlock();//释放锁}
}复制代码

ConcurrentLinkedQueue

Java提供的线程安全的 Queue 可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是 BlockingQueue, 非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。 阻塞队列可以通过加锁来实现,非阻塞队列可以通过CAS操作实现。

ConcurrentLinkedQueue使用链表作为其数据结构。 ConcurrentLinkedQueue应该算是在高并发环境中性能最好的队列了。 它之所有能有很好的性能,是因为其内部复杂的实现。

ConcurrentLinkedQueue 主要使用CAS非阻塞算法来实现线程安全。 适合在对性能要求相对较高,对个线程同时对队列进行读写的场景, 即如果对队列加锁的成本较高则适合使用无锁的ConcurrentLinkedQueue来替代。

BlockingQueue

java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:

  • FIFO 队列 :LinkedBlockingQueue、ArrayBlockingQueue(固定长度)

  • 优先级队列 :PriorityBlockingQueue

提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容; 如果队列为满 put() 将阻塞,直到队列有空闲位置。

使用 BlockingQueue 实现生产者消费者问题

public class ProducerConsumer {private static BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);private static class Producer extends Thread {@Overridepublic void run() {try {queue.put("product");} catch (InterruptedException e) {e.printStackTrace();}System.out.print("produce..");}}private static class Consumer extends Thread {@Overridepublic void run() {try {String product = queue.take();} catch (InterruptedException e) {e.printStackTrace();}System.out.print("consume..");}}
}
public static void main(String[] args) {for (int i = 0; i < 2; i++) {Producer producer = new Producer();producer.start();}for (int i = 0; i < 5; i++) {Consumer consumer = new Consumer();consumer.start();}for (int i = 0; i < 3; i++) {Producer producer = new Producer();producer.start();}
}
produce..produce..consume..consume..produce..consume..produce..consume..produce..consume..复制代码

转载于:https://juejin.im/post/5ce8e3f26fb9a07ec27b7f57

Java并发容器,底层原理深入分析相关推荐

  1. Java并发基石CAS原理以及ABA问题

    在学习CAS之前,先从一个简单的案例入手,进而引出CAS的基本使用: 1.基于CAS的网站计数器 需求: 我们开发一个网站,需要对访问量进行统计,用户每发送一次请求,访问量+1,如何实现? 我们模拟有 ...

  2. Java多线程系列(七):并发容器的原理,7大并发容器详解、及使用场景

    之前谈过高并发编程系列: 高并发编程系列:4种常用Java线程锁的特点,性能比较.使用场景 高并发编程系列:CountDownLatch.Semaphore等4大并发工具类详解 高并发编程系列:4大J ...

  3. 死磕Java并发:J.U.C之Java并发容器:ConcurrentLinkedQueue

    作者:chenssy 来源:Java技术驿站 要实现一个线程安全的队列有两种方式:阻塞和非阻塞.阻塞队列无非就是锁的应用,而非阻塞则是CAS算法的应用.下面我们就开始一个非阻塞算法的研究:Coucur ...

  4. Java并发指南14:Java并发容器ConcurrentSkipListMap与CopyOnWriteArrayList

    原文出处http://cmsblogs.com/ 『chenssy』 到目前为止,我们在Java世界里看到了两种实现key-value的数据结构:Hash.TreeMap,这两种数据结构各自都有着优缺 ...

  5. Java并发编程学习 + 原理分析(建议收藏)

    总结不易,如果对你有帮助,请点赞关注支持一下 微信搜索程序dunk,关注公众号,获取博客源码 Doug Lea是一个无私的人,他深知分享知识和分享苹果是不一样的,苹果会越分越少,而自己的知识并不会因为 ...

  6. java 传绝对路径无效_【Java并发005】原理层面:volatile关键字全解析

    一.前言 在Java 5之前,volatile是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果. 在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽 ...

  7. Java集合框架底层原理

    Java集合框架 Java集合框架 List集合 ArrayList底层实现原理 ArrayList数组扩容技术(数组拷贝) 扩容大小 查询和删除 集合中的泛型 LinkedList Vector 线 ...

  8. java锁的底层原理

    知识整理 Synchronized 内置锁,JVM级别 使用 底层 锁升级过程.CAS操作的缺点[替换线程和copy mw] 优化 代码优化:同步代码块.减少锁粒度.读锁并发 JDK自带 偏置锁.轻量 ...

  9. Java 并发编程-不懂原理多吃亏(送书福利)

    作者 | 加多 关注阿里巴巴云原生公众号,后台回复关键字"并发",即可参与送书抽奖! ** 导读:并发编程与 Java 中其他知识点相比较而言学习门槛较高,从而导致很多人望而却步. ...

  10. Java并发编程—AQS原理分析

    目录 一.AQS原理简述 二.自定义独占锁及共享锁 三.锁的可重入性 四.锁的公平性 五.惊群效应 AQS全称AbstractQueuedSynchronizer,它是实现 JCU包中几乎所有的锁.多 ...

最新文章

  1. SpringBoot自定义参数验证器
  2. 信息安全研究之数据安全专题
  3. 配置apache+php环境详解
  4. SharePoint服务器修改域和机器名
  5. 前端学习(1746):前端调试值之时间监听
  6. 用html制作广告图片切换效果,基于jquery实现图片广告轮换效果代码
  7. Oracle Database 11g Express Edition使用限制,与其他版本的区别
  8. 简便无刷新文件上传系统
  9. Django路由控制
  10. cpu单核性能测试软件,CPU常用跑分软件 你知道那些?
  11. selected和checked区别
  12. XILINX Ultrascale/Ultrascale+ 高速收发器时钟MGTHREFCLK原语调用
  13. 四、RNN模型 与 NLP应用 —— Stacked RNN
  14. 数据挖掘与机器学习经典书目
  15. 服务器部署邮件功能_真正连续部署的功能标志
  16. JESD204接口调试总结——Xilinx JESD204B IP testbench解析
  17. python使用win32后台鼠标点击梦幻西游(只用于开学习技术)新手学习
  18. 最新资讯 针对移动手机漏洞与安全支付
  19. 餐饮门店数字化转型|餐厅管理系统小程序
  20. python实现shamir秘密共享算法

热门文章

  1. idea 项目启动找不到页面问题和run/debug只能启动一个的问题
  2. Can not find the tag library descriptor for http://java.sun.com/jsp/jst1/core
  3. windwos::mutex
  4. linux 单机跨进程通信
  5. SSH框架的简化(struts2、spring4、hibernate5)
  6. Java使用笔记之stream和sorted使用
  7. 36. 打印数组的主次对角线
  8. 配置springMVC时出现的问题
  9. Android ListView 指定显示最后一行
  10. ZooKeeper搭建实验