`

Java并发包学习

 
阅读更多
1. ConcurrentHashMap
顾名思义,ConcurrentHashMap是应用于高并发场景的HashMap.
由于HashMap是非线程安全的,而HashTable在HashMap的基础上使用了Synchronized, 以此来保证线程安全。但问题在于,HashTable的Synchronized是针对整个Hash表的,即每次锁定整张表让该线程独占,这样虽然保证了线程安全,确造成了巨大的浪费。
综上,ConcurrentHashMap出现了。

ConcurrentHashMap与HashTable的主要区别在于:锁的粒度。

ConcurrentHashMap允许多个线程同时对Hash表做修改操作,这是因为ConcurrentHashMap使用了“锁分离”。即ConcurrentHashMap默认将整个Hash表分为16个“段”(segment),每个segment其实就是一个小的HashTable, 他们有自己的锁。所以只要不同线程的修改操作在不同的segment上,他们就可以并发地执行了。

ConcurrentHashMap的有些方法也需要跨段,如size(), containsValue(),他们可能需要锁定整个表而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按顺序”的目的是为了避免出现死锁。

在ConcurrentHashMap的内部,Segment数组是final的,并且其成员变量也是final的,但Segment数组的成员并不一定是final的。这可以保证不会出现死锁,因为获得每个Segment的锁时的顺序是固定的,不变性是多线程编程中很重要的。

ConcurrentHashMap对于读操作并不加锁,所以多个线程的读操作完全可以并发地进行。

对于增加元素时导致的Hash冲突,ConcurrentHashMap与HashMap使用了相同的解决办法,都是将hash值相同的节点放在一个链表中,但与HashMap不同的是,ConcurrentHashMap使用了多个Hash表,也就是段(Segment)。

ConcurrentHashMap的迭代器与传统的不同:当iterator被创建后,如果集合再发生改变,ConcurrentHashMap的迭代器不会抛出ConcurrentModificationException,而是iterator仍然使用原来老的数据,而写线程也可以并发地完成,在iterator遍历完成后,在将头指针替换为新的数据。

ConcurrentHashMap的主要实体类有3个:ConcurrentHashMap(整个Hash表),Segment(段),HashEntry(节点)
ConcurrentHashMap的重要方法:
1)get(Object key):与HashTable的不同是将粒度细化到了Segment上:
首先判断当前Segment的元素个数是否为0,这是为了避免不必要的搜索。
然后,得到头结点,根据hash和key逐个判断是否是指定的值,如果是并且非空就找到了,直接返回。

代码如下:
V get(Object key) {
            int hash = hash(key.hashCode());
            if (count != 0) { // read-volatile
                HashEntry e = getFirst(hash);
                while (e != null) {
                    if (e.hash == hash && key.equals(e.key)) {
                        V v = e.value;
                        if (v != null)
                            return v;
                        return readValueUnderLock(e); // recheck

                    }
                    e = e.next;
                }
            }
            return null;
        }

V readValueUnderLock(HashEntry e) {
            lock();
            try {
                return e.value;
            } finally {
                unlock();
            }
        }

注意加粗部分关键代码:
由于ConcurrentHashMap不允许空的key或者空的value, 为什么v的值可能为null呢?这说明有其他的线程正在改变v的值,而get方法并未加锁,所以在这时,读的时候先加锁,再读,在释放锁。

所以说,ConcurrentHashMap的读(get)操作并不是完全不加锁的,在特定的情况(v为null时)也会加锁。

2)put(K key, V value):
put操作一上来就锁定整个Segment, 这当然是为了线程安全,因为修改数据是不能并发的。

3) remove方法与put类似。

2. CopyOnWriteArrayList
CopyOnWriteArrayList是一个线程安全的,并且在读操作时无所的ArrayList.

CopyOnWriteArrayList的核心思想是,利用高并发时,往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面进行修改,然后将新的集合赋值给旧的引用,并通过volatile保证其可见性,在写操作时是加锁的。

1)add(E):
add方法并没有使用Synchronized, 它通过使用ReentrantLock来保证线程安全。
与ArrayList不同的是,它每次会创建一个新的Object数组,此数组的大小是当前数组大小加1,然后将之前数组中的内容复制到新的数组中,并将新增加的对象放入输入末尾,最后做引用切换,将新创建的数组对象赋值给全局的数组对象。

2) remove(E):
与add方法一样,也是通过ReentrantLock来保证其线程安全

3) get(int):
直接获取当前数组对应位置的元素,但此方法没有加锁,所以可能会读到脏数据。但相对而言,性能会非常高,对于读多写少且脏数据影响不大的场景而言,CopyOnWriteArrayList是不错的选择。

3. CopyOnWriteArraySet
CopyOnWriteArraySet是基于CopyOnWriteArrayList的实现,唯一不同在于add时,CopyOnWriteArraySet调用的是CopyOnWriteArrayList的addIfAbsent方法。addIfAbsent方法同样采用了锁保护,并创建一个新的大小+1的Object数组,如Object数组已有了当前元素,则直接返回,如没有则把新元素放到Object数组的尾部,并返回。

综上,CopyOnWriteArraySet在每次add时都要遍历数组,性能会略低于CopyOnWriteArrayList.

4.ArrayBlockingQueue
ArrayBlockingQueue是一个基于数组的,先进先出,线程安全的Queue,其特点是可以实现指定时间的阻塞读写,且其容量是可以限制的。

5. ConcurrentSkipListMap
1) ConcurrentHashMap与ConcurrentSkipListMap的性能测试
在4个线程,1.6万数据的条件下,ConcurrentHashMap的存取速度比ConcurrentSkipListMap快4倍左右。
那么ConcurrentSkipListMap的好处在哪呢?
    -- ConcurrentSkipListMap的key是有序的
    -- ConcurrentSlipListMap支持更高的并发。ConcurrentSkipListMap的存取时间是logN, 和线程数几乎无关。也就是说,在数据量一定的情况下,并发的线程数越多,ConcurrentSkipListMap越能体现出优势。
2)使用建议
在非多线程的情况下,应当尽量使用TreeMap。
此外,对于并发性相对较低的并发程序,可以使用Collections.synchronizedSortedMap将TreeMap进行包装,也可以提供更好的效率。
对于高并发的程序,应当使用ConcurrentSkipListMap。
3)什么是SkipList
Skip List(跳表)是一种可以代替平衡树的数据结构,默认是按照key值升序的。
Skip List让已排序的数据分布在多层链表中,以0--1随机数决定一个数据是否向上攀升,是通过“空间来换时间”的算法。
在每个节点中增加了向前的指针,在插入、删除、查找时,可以忽略一些不可能涉及到的节点,从而提高了效率。
SkipList比自平衡树简单,因为Skip List只需要从概率上保持数据结构平衡,而自平衡树需要严格保持数据结构平衡。
虽然SkipList和红黑树都有相同的时间复杂度LogN, 但Skip List的N会相对小很多,所以更快。Skip List在空间上也比红黑树节省,一个节点平均只需要1.333个指针。

4)Skip List的性质
    -- 由许多层结构组成,level是通过一定的概率随机计算出来的
    -- 每一层都是一个有序的链表,默认是升序,也可以根据创建映射时所提供的Comparator进行排序。
    -- 最底层(level 1)的链表包含了所有的元素
    -- 如果一个元素出现在Level i的链表中,那么它在level i之下的链表也会出现。
    -- 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。

5)什么是ConcurrentSkipListMap
ConcurrentSkipListMap提供了一种线程安全的并发访问的排序映射表。
内部是SkipList(跳表)结构实现,在理论上能够在O(logN)时间内完成查找、插入、删除操作。
注:在调用ConcurrentSkipListMap的size方法时,由于多个线程可以同时对映射表进行操作,所以映射表需要遍历整个链表才能返回元素个数,这个操作是O(logN)

6) ConcurrentSkipListMap的存储结构



ConcurrentSkipListMap用到了Node和Index两种节点的存储方式,通过volatile关键字实现了并发的操作。

7)ConcurrentSkipListMap的查找



红色虚线,表示查找的路径;蓝色向右的箭头表示right引用;黑色向下箭头表示down引用。
  • 大小: 135.9 KB
  • 大小: 154.3 KB
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    java 并发学习总结

    3. **同步工具类**:Java并发包`java.util.concurrent`中的工具类,如`Semaphore`(信号量)、`CyclicBarrier`(回环栅栏)、`CountDownLatch`(倒计时器)和`FutureTask`(未来任务)等,提供了更灵活的线程同步和...

    深入学习java并发包ConcurrentHashMap

    《深入学习Java并发包ConcurrentHashMap》 Java并发包中的ConcurrentHashMap是线程安全的散列映射,它是HashMap的并发版本,适用于多线程环境。与HashMap不同,ConcurrentHashMap采用了一种称为分段锁(Segment)的...

    Java 2全方位学习

    书中会讲解线程的创建、同步机制(如synchronized关键字、wait/notify机制)、死锁问题,以及Java并发包(java.util.concurrent)中的高级工具,如ExecutorService和Future,帮助开发者构建多线程程序并确保其正确性...

    JavaConcurrency:这是一个用于Java并发包的学习程序

    Java并发包(java.util.concurrent)提供了一系列的类和接口,旨在简化多线程环境下的编程,提高应用程序的性能和可伸缩性。 首先,我们要了解Java中的线程。线程是程序执行的最小单元,一个进程可以有多个线程同时...

    java语言学习六大要点

    此外,熟悉Java并发包(java.util.concurrent)中的工具类,能帮助简化多线程编程。 6. 了解网络编程: 虽然大多数开发者不会直接编写底层网络程序,但理解网络编程的基本原理,如TCP/IP协议、套接字(Socket)通信...

    Java学习指南(第4版)(下册)中文高清完整版PDF

    介绍Java内建的线程工具和并发包; 学习文本处理和强大的正则表达式API; 编写高级的网络化或基于Web的应用和服务; 使用Java组件架构,构建图形化的Java应用; 探讨丰富的新桌面API; 使用XML和XSLT,包括XML ...

    学习java必看

    11. **Java标准库**:深入学习Java的标准库,如util包中的各种工具类,lang包中的基础类,以及并发包(concurrent)中的并发工具。 12. **框架与库**:学习流行的Java框架如Spring、Hibernate、MyBatis等,以及如何...

    concurrent-resource-test:Java并发包源码学习

    Java并发包(java.util.concurrent)提供了丰富的类和接口,用于处理多线程环境下的同步、互斥以及线程间的协作。这个"concurrent-resource-test"项目很可能是为了深入理解和实践Java并发包中的源码而设计的。下面...

    本项目适合于新手小白入门快速了解掌握Java 大部分必备知识,用于Java的学习与进阶,也可以用于回忆巩固Java 基础用法语法

    Study1 类: Java 父类与子类继承 Study2 类:Java 重载与重写 ...Study15 类:Java 并发包 Study16 类:Java 动态代理 Study17 类:Java 字节流与字符流 Study18 类:Java NIO与NIO.2 Study19 类:Java Socket编程

    java学习资料

    Java并发包(java.util.concurrent)提供了丰富的工具,如Semaphore(信号量)、CountDownLatch(计数器)和CyclicBarrier(循环栅栏)等,用于协调多线程间的同步和通信。 最后,Spring框架是Java开发中广泛使用的...

    java-core:JAVA集合包,线程池,并发包,NIO等API及链接相关学习

    这个名为"java-core"的资源集重点关注了几个关键领域:集合框架、多线程、线程池、并发包以及非阻塞I/O(NIO)。让我们逐一深入探讨这些主题。 1. **Java集合框架**: Java集合框架是处理对象集合的一组接口和类,...

    Java并发编程学习笔记

    Java并发包(java.util.concurrent)也提供了丰富的并发工具,如线程安全集合(如ConcurrentHashMap)、阻塞队列(如ArrayBlockingQueue)、信号量(Semaphore)、并发集合(如CopyOnWriteArrayList)和各种执行器...

    JUC2019.6V1.5.zip

    juc笔记:这些知识点是怎么积累进来的呢,下面我以JUC的学习过程为例子...的,然后要知道java并发包包括哪些知识点,这些知识点在平常中有哪些重要的运用,简单来说,这个学习 方法就三个点,是什么?为什么?怎么办?

    深入学习:Java多线程编程

    4. **并发工具类**:Java的并发包(java.util.concurrent)包含许多高效并发工具,如CountDownLatch、CyclicBarrier、Future和Callable,它们在多线程编程中扮演重要角色。 5. **并发集合**:线程安全的集合类,如...

    Java最新学习路线.docx分享给需要的同学

    1. 多线程和并发编程:学习使用Java并发包(如java.util.concurrent)处理多线程编程,了解线程池、锁机制和并发集合等。 2. 性能优化:学习分析和调优Java应用程序的性能,了解内存管理、垃圾回收、性能监测和调优...

    2023java最新学习路线.docx

    在这一阶段,对Java技术进行深入研究,重点是多线程和并发编程,了解Java并发包和线程池,以及锁机制和并发集合。学习性能优化,包括内存管理和垃圾回收,使用性能监测工具进行调优。掌握反射和字节码操作,实现动态...

    java并发编程从入门到精通

    随着Java 5.0版本的发布,Java并发包(java.util.concurrent)的引入极大地简化了并发编程的难度,这个并发包提供了大量用于并发编程的高级工具,包括线程池、并发集合、原子变量、锁、同步器等。这些工具的出现让...

    natural-selection-simulation:使用Java并发包的自然选择模拟

    《自然选择模拟:Java并发包的应用解析》 自然选择模拟是一种用计算机程序模拟生物进化过程的方法,它基于达尔文的自然选择理论,用于理解和探索复杂系统的行为。在本项目"natural-selection-simulation"中,开发者...

Global site tag (gtag.js) - Google Analytics