Java 同步容器和并发容器

同步容器和并发容器

先解释一下概念,说下自己的理解:
同步和异步:同步就是串行,两个操作要串行先后一起完成后面的操作要等前面的完成并返回结果才能往下走,而异步则后面的操作可以不用等前面的操作是否完成就能执行自己的操作

阻塞和非阻塞:阻塞就是加锁,某一线程拿到锁之后其他线程就只能等待,非阻塞就是不加锁而使用CAS原子类操作来完成,这样每个线程的操作就不会阻塞其他线程的执行

同步容器

之前同步一般有两种操作,1.使用线程安全的类比如Vector和Hashtable,他们本身就加了synchronized关键字来保证同步操作。

线程安全--线程不安全
Vector---ArrayList
Hashtable--HashMap

2.使用Collections来做包装,(其实Collections就是对集合Collection的静态工具类,其中包含了很多方法,同步就是其中之一 示例链接,
Collections工具类中提供了多个synchronizedXxx方法,该方法返回指定集合对象对应的同步对象,从而解决多线程并发访问集合时线程的安全问题。HashSet、ArrayList、HashMap都是线程不安全的,如果需要考虑同步,则使用这些方法。这些方法主要有:synchronizedSet、synchronizedSortedSet、synchronizedList、synchronizedMap、synchronizedSortedMap

ArrayList s=new ArrayList<>();
List list= Collections.sychronizedList(s);

这两种方法都比较效率低,在访问时都相当于是单线程访问

并发容器

并发容器就是为了解决同步容器性能差的问题,对应与同步容器提供的类,并发容器也提供了相应的:

同步容器--容器--并发容器
Vector--->ArrayList<---CopyOnWriteArrayList--这的思想就是读写分离
Hashtable-->HashMa<---ConcurrentHashMap--这个的思路就是降低粒度

CopyOnWriteArrayList

CopyOnWriteArrayList底层是由数组实现的,当执行写操作时,比如set/add/remove,CopyOnWriteArrayList都是使用了ReentrantLock来加锁并将数组重新复制一份,当执行写操作完成后再将原来的数组指向修改了的数组
实际上如果是大量的写操作可能同步容器的性能都会比CopyOnWriteArrayList要高,同时每次拷贝也需要消耗内存,大量的写操作就意味着大量的内存消耗

ConcurrentLinkedQueue

ConcurrentLinkedQueue底层是由链表实现的。以下摘自:https://blog.csdn.net/qq_38293564/article/details/80798310

使用 CAS 原子指令来处理对数据的并发访问,这是非阻塞算法得以实现的基础。
head/tail 并非总是指向队列的头 / 尾节点,也就是说允许队列处于不一致状态。 这个特性把入队 / 出队时,原本需要一起原子化执行的两个步骤分离开来,从而缩小了入队 / 出队时需要原子化更新值的范围到唯一变量。这是非阻塞算法得以实现的关键。
由于队列有时会处于不一致状态。为此,ConcurrentLinkedQueue 使用三个不变式来维护非阻塞算法的正确性。
以批处理方式来更新 head/tail,从整体上减少入队 / 出队操作的开销。
为了有利于垃圾收集,队列使用特有的 head 更新机制;为了确保从已删除节点向后遍历,可到达所有的非删除节点,队列使用了特有的向后推进策略。

简单来说,就是ConcurrentLinkedQueue是使用非阻塞即不使用同步操作,而是使用CAS原子指令来保证数据一致性,同时ConcurrentLinkedQueue前后两端的节点允许出现不一致的状态因为head/tail 并非总是指向队列的头 / 尾节点

BlockingQueue

前面解释了阻塞和非阻塞的区别,BlockingQueue就是一个阻塞队列,但不是每一个方法都是阻塞的,其中最主要使用的阻塞方法就是:put()放和take()取

ConcurrentHashMap

本博客为Swagger-Ranger的笔记分享,文中源码地址: https://github.com/Swagger-Ranger
欢迎交流指正,如有侵权请联系作者确认删除: liufei32@outlook.com

原文地址:https://www.cnblogs.com/Swagger-Ranger/p/10669959.html