concurrentHashMap及CopyOnWriteArrayList、Collections.synchronizedList

本文主要是介绍concurrentHashMap及CopyOnWriteArrayList、Collections.synchronizedList,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

concurrentHashMap是线程安全的hashmap,在java1.8之前的版本,concurrentHashMap结构为一个segment数组默认初始长度为16,每一个segment都是一个hashmap,Segment继承了ReentranLock来加锁,它的put、remove操作都需要获取segment数组的reentranLock锁,get操作不加锁,因为每个键值对的value是volitale,具有可见性,当它被修改后会及时更新进内存可以被其他线程发现,所以get操作不加锁。它的size()即获取键值对的总数量的计算方法是,会先不加锁获取整个segments的每个hashmap长度计算加和2次,如果两次长度相同则认为它的长度为该值,否则会将每一个segment锁住,然后分别计算它们的长度,进行求和,因此在1.8之前的版本获取size最多计算3次。

而hashmap中size()方法返回的是size字段的值,当put时,size会加一,remove时size会减一。

1.7中的结构如图

3

在jdk1.8后,不再使用segments这种方式,而是使用了hashmap+synchronized+cas来实现线程安全。

以put方法为例:需要指出的是put操作中使用到了for循环,因为在table初始化和casTabAt的过程中可能有其它线程也在进行操作,需要不断尝试。

(1)首先会通过spread()方法计算key的hash值,与HashMap中不同的是它会将结果再与一个Hash_Base做&操作 hash^(hash>>>16) & Hash_Base;之后会判断数组table是否存在,如果不存在将会去创建,再这一过程中可能会存在多个线程同时去创建数组的情况,此时会使用cas的方式,将SIZECTL修改为-1,修改成功则说明获取了创建数组的锁,进行数组创建。

(2)如果数组存在,则会使用hash&(n-1)计算该key对应的数组下标,n为当前数组的长度,如果数组下标对应的node为null,则会使用casTabAt的方式根据key、value创建新的节点,尝试将新节点放入该下标之中,成功则跳出循环

(3)如果数组下标中n头节点node不为null,且它的hash状态是move则说明当前数组正在扩容,当前线程也会去帮助扩容。

(4)若下标node不为null且数组并未扩容,则尝试进入到synchronized方法块中,进行后续操作。该synchrozed修饰的是数组下标中的头节点node对象,想进入代码块,必须获取该节点的monitor对象使用权。进行代码块后,会根据下标节点存储的是链表还是红黑树进行遍历,如果存在key相同的节点则将value进行覆盖,否则创建新节点进行插入,红黑树插入后还会进行相应旋转。

(5)之后会判断链表的长度是否达到了8个,如果是则转化为红黑树。

(6)最后会调用addCount方法,将concurrentHashMap的长度加一,在这个里面还会判断是否需要扩容。ConcurrentHashMap中有两个元素去记录当前元素的总个数,一个是long 类型baseCount、一个CounterCells数组。首先会尝试使用cas的方式对baseCount进行+1操作;如果失败,当counterCells存在则根据Thread探针和m(counterCells数组长度-1)进行&操作获取数组对应下标元素,使用cas的方式将其中的元素值进行+1;

若前面的条件都不满足或执行失败则会进入到一个循环方法之中去,counterCells如果为null则会创建初始长度为2,之后会使用根据thread探针与counerCells长度-1进行与操作获取下标,如果该下标元素存在,则使用cas对其中数据进行+1,否则创建counterCell对象,初始值为1,以cas的形式将其放入对应下标,如果成功则跳出循环,如果多次失败则说明有其他线程也在执行长度+1,且发生了多次碰撞,此时将counterCells数组扩容长度×2,在执行相同的操作直到成功+1为止;

之后会判断当前数组中元素是否已到达负载因子限制,是的话数组会进行扩容。在扩容时,涉及到了节点重新映射的过程,它支持多线程的协助操作,在这个过程中多线程是分段领取任务,比如说当前数组长度为32,则线程1领取24-31,这8个下标的重hash工作,线程2领取16-23下标中元素的重hash,领取的时候是使用cas的方式尝试修改当前没有被领取最大下标的值,比如一开始是cas(filed,31,23),修改成功之后就说明之后0到23还没人认领。

对于数组每一个下标的链表或红黑树来说,和hashmap类似,也是通过key的hash值与原数组长度n取与看是否等于0,将节点分为low和high两个list,然后将list放入新数组原下标i和i+n的位置。

1.8 size()方法

调用sumCount()方法,它其实是把baseCount的值和countercells数组中元素的值进行了加和,不过baseCount和countercell中存储的是long类型元素,size的返回值是int,当加和返回值大于int最大限制时,直接返回的Integer.MAX_VALUE,在ConcurrentHashMap中推荐使用的获取元素数量的方法是MappingCount()方法,它也是调用了sumCount方法,不过返回值是long类型,不会损失精度。

为什么在1.8即使用了cas也使用了synchronized

通过cas的方式来尝试获取锁,当前线程并没有被阻塞,但如果并发量比较大的情况下,对cpu资源消耗比较大,以put方法为例,cas主要用于发生频率比较小或执行比较快的的情景下,比如像创建数组、创建数组下标头节点、数组扩容、size的变化等,避免了将线程阻塞再唤醒造成的资源消耗,而像数组下标链表及红黑树中元素的put这种比较复杂且耗时操作还是使用的synchronized进行加锁,避免大量线程长时间进行自旋获取锁造成cpu资源浪费。

于1.7中使用reentrantLock对segment整个hashmap进行加锁,1.8 使用cas+syncronized的方式,且synchronized仅对数组下标头节点元素加锁,更灵活且粒度更小,减少了大量线程在获取同一个锁时发生阻塞的概率,同时使用cas也避免了大量线程在不必要的地方陷入过早阻塞,影响执行速度

hashtable中put、get、size所有方法都用了synchronized加锁。

4

Java并发编程总结4——ConcurrentHashMap在jdk1.8中的改进 - everSeeker - 博客园

http://www.importnew.com/28263.html

java为我们提供了一些线程同步的list如Collections.SynchronizedList和CopyOnWriteArrayList

CopyOnWriteArrayList内部有一个ReenTranLock,读操作不加锁,它的set、add、remove操作是需要加锁的,并且会创建一个新的数组,进行操作后将新数组赋值给原数组引用,因此写操作效果很差

List<String> arrayList = Collections.synchronizedList(new ArrayList<String>());它的读写操作都会被synchronized关键字修饰,读性能很差

java.util.concurrent包下

Callable、Future、Executor

package callable;import stu.people;import java.util.concurrent.*;/*** The xxx class for xxx.** @author sunweiliang* @version 1.0, 2019/3/11* @since untitled 1.0.0*/
public class CallableTest  {public static void main(String[] args) {// ExecutorService es1 = Executors.newCachedThreadPool();ExecutorService es = Executors.newCachedThreadPool();ExecutorService es1 = Executors.newFixedThreadPool(1);ExecutorService es2 = Executors.newSingleThreadExecutor();ExecutorService es3 = Executors.newScheduledThreadPool(10);callableThread cThread = new callableThread();Future<people> future = es.submit(cThread);//ScheduledExecutorServicetry{people str = future.get();System.out.println(str);} catch(Exception e) {}}}class callableThread implements Callable<people> {@Overridepublic people call() throws Exception {people p = new people();p.setName("swl");p.setOld(123);return p;}
}

ReenTranLock、Condition

Executors、ThreadPoolExecutor、ScheduledThreadPoolExecutor

ConcurrentHashMap、CopyOnWriteArrayList

这篇关于concurrentHashMap及CopyOnWriteArrayList、Collections.synchronizedList的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/763503

相关文章

Java重修笔记 第四十九天 Collections 工具类

Collections 工具类 1. public static void reverse(List<?> list)         反转集合中元素的顺序 2. public static void shuffle(List<?> list)         将集合里的元素顺序打乱 3. public static <T extends Comparable<? super T>>

Java学习:ConcurrentHashMap实现一个本地缓存

文章来源: https://www.cnblogs.com/parryyang/p/5779984.html 本片文章不考虑效率问题,可以当做用来学习ConcurrentHashMap。 ConcurrentHashMap融合了Hashtable和HashMap二者的优势。Hashtable是做了线程同步,HashMap未考虑同步。所以HashMap在单线程下效率较高,Ha

Java 面试题:从源码理解 ThreadLocal 如何解决内存泄漏 ConcurrentHashMap 如何保证并发安全 --xunznux

文章目录 ThreadLocalThreadLocal 的基本原理ThreadLocal 的实现细节内存泄漏源码使用场景 ConcurrentHashMap 怎么实现线程安全的CAS初始化源码添加元素putVal方法 ThreadLocal ThreadLocal 是 Java 中的一种用于在多线程环境下存储线程局部变量的机制,它可以为每个线程提供独立的变量副本,从而避免多个线

Python模块 - Collections

collections的常用类型有: 计数器(Counter) 双向队列(deque) 默认字典(defaultdict) 有序字典(OrderedDict) 可命名元组(namedtuple) Counter() Counter 作为字典(dict)的一个子类用来进行hashtable计数,将元素进行数量统计、计数后返回一个字典,键值为元素:值为元素个数 s = 'abcbcac

【不安全的集合类】同步容器(如ConcurrentHashMap)、并发集合(如CopyOnWriteArrayList)

文章目录 一、List的线程不安全二、Set的线程不安全三、Map的线程不安全 日常我们用到的集合的情况会很多,在单线程的情况下,不用考虑到线程安全的问题,但是如果在多线程开发的过程中,我们该选择哪一种类型来保证线程安全性呢 ? 一、List的线程不安全 我们先来看一个例子: package com.atguigu.signcenter.nosafe;import j

【编程底层思考】详解Java中Collections工具类

Java 的 java.util.Collections 类是一个包装类,它包含了一系列静态方法来操作或返回集合对象。这个类提供了对集合框架的扩展,使得集合的使用更加灵活和强大。以下是 Collections 类的一些关键特性和用途: 静态方法 排序:sort(List list) 可以对列表进行自然顺序排序,sort(List list, Comparator c) 允许使用自定义的比较器进

23. 如何使用Collections.synchronizedList()方法来创建线程安全的集合?有哪些注意事项?

Collections.synchronizedList() 方法用于将一个普通的 List 包装成线程安全的 List。通过这个方法生成的 List,所有的访问和修改操作都会被自动加锁,从而确保在多线程环境下对集合的并发访问是安全的。 如何使用 Collections.synchronizedList() 创建线程安全的集合 以下是使用 Collections.synchronizedL

第一章 集合框架和泛型(ArrayList/LinkedList/HashSet/HashMap/泛型集合/Collections算法类)

第一章 集合框架和泛型 一、Collection 1、Collection 接口存储一组不唯一,无序的对象 二、List List 接口存储一组不唯一,有序(插入顺序)的对象 1.ArrayList 实现了长度可变的数组,在内存中分配连续的空间优点:遍历元素和随机访问元素的效率比较高ArrayList类是List接口的一个具体实现类ArrayList对象实现了可变大小

ConcurrentHashMap扩容原理 | 存储流程 | 源码探究

新人写手,代码菜鸡;笔下生涩,诚惶诚恐。 初试锋芒,尚显青涩;望君指点,愿受教诲。  本篇文章将从源码的层面,探讨ConcurrentHashMap的存储流程以及扩容原理 Java版本为JDK17,源代码可能与其他版本略有不同 推荐阅读:HashMap实现原理、扩容机制  一、构造函数 1.1 无参构造函数 ConcurrentHashMap的无参构造函数是一个空方法 pub

2024年最新Java面试宝典系列-Collections集合篇1

Java中的集合类有哪些?它们的特点是什么 List:有序集合,允许重复元素,实现类如ArrayList、LinkedList。Set:无序集合,不允许重复元素,实现类如HashSet、TreeSet。Map:键值对集合,一个键对应一个值,实现类如HashMap、Hashtable、TreeMap。 ArrayList、LinkedList与Vector的区别是什么 ArrayList:基于