转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
JDK提供的容器,大部分在java.util.concurrent包中
- ConcurrentHashMap:线程安全的HashMap
- CopyOnWriteArrayList:线程安全的List,在读多写少的场合性能非常好,远好于Vector
- ConcurrentLinkedQueue:高效的并发队列,使用链表实现,可以看作一个线程安全的LinkedList,是一个非阻塞队列
- BlockingQueue:这是一个接口,JDK内部通过链表、数组等方式实现了该接口。表示阻塞队列,非常适合用于作为数据共享的通道
- ConcorrentSkipListMap:跳表的实现,是一个Map,使用跳表的数据结构进行快速查找
ConcurrentHashMap#
- HashMap是线程不安全的,并发场景下要保证线程安全,可以使用Collections.synchronizedMap()方法来包装HashMap,但这是通过使用一个全局的锁来同步不同线程间的并发访问,因此会带来性能问题
- 建议使用ConcurrentHashMap,不论是读操作还是写操作都能保证高性能:读操作(几乎)不需要加锁,而写操作时通过锁分段(这里说的是JDK1.7?)技术,只对所操作的段加锁而不影响客户端对其他段的访问
CopyOnWriteArrayList#
//源码
public class CopyOnWriteArrayList<E>
extends Object
implements List<E>, RandomAccess, Cloneable, Serializable
- 在很多应用场景中,读操作可能会远远大于写操作
- 我们应该允许多个线程同时访问List内部数据(针对读)
- 与ReentrantReadWriteLock读写锁思想非常类似,即读读共享、写写互斥、读写互斥、写读互斥
- 不一样的是,CopyOnWriteArrayList读取时完全不需要加锁,且写入也不会阻塞读取操作,只有写入和写入之间需要同步等待。
CopyOnWriteArrayList是如何做到的#
CopyOnWriteArrayList类的所有可变操作(add,set 等等)都是通过创建底层数组的新副本来实现的。当 List 需要被修改的时候,并不修改原有内容,而是对原有数据进行一次复制,将修改的内容写入副本。写完之后,再将修改完的副本替换原来的数据,这样就可以保证写操作不会影响读操作了。- 从
CopyOnWriteArrayList的名字就能看出CopyOnWriteArrayList是满足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写入操作的实现 在添加集合的时候加了锁,保证同步,避免多线程写的时候会copy出多个副本

CPU Cache通常分为三层,分别叫L1,L2,L3 Cache
工作方式: 先复制一份数据到CPUCache中,当CPU需要用的时候就可以从CPUCache中读取数据,运算完成后,将运算得到的数据,写回MainMemory中,此时,会出现内存缓存不一致的问题,例子:执行了i++,如果两个线程同时执行,假设两个线程从CPUCach中读取的i=1,两个线程做了1++运算完之后再写回MainMemory,此时i=2 而正确结果为3
操作系统,通过内存模型MemoryModel定义一系列规范来解决这个问题







