1.CopyOnWriteArrayList
1.1 并发修改时保证线程安全:
- 通过ReentrantLock实现多个线程并发修改时的线程安全同步(添加元素的同时,不允许删除)
- 添加新元素:list.add("")
- 按照指定下标替换元素:list.set(index, element)
- 按照指定下标删除元素:list.remove(0)
1.2 并发读取:
没有加锁,允许多个线程同时并发读取;但是读取时,可能产生脏读(读取的同时,允许写入操作)。
1.3 CopyOnWrite思想:
修改时将原数组内容复制Copy到新数组内,在新数组内修改,然后替换
2.CopyOnWriteArraySet
CopyOnWriteArraySet内部通过一个CopyOnWriteArrayList实现
3.BlockingQueue阻塞队列
阻塞队列:由两个线程,分别进行读写(task和put)操作;读取时,不允许写入,如果队列为空,则读取线程阻塞;写入时,不允许读取,如果队列已满,则写入线程阻塞;
ArrayBlockingQueue---有界队列
LinkedBlockingQueue---无界队列
4.ConcurrentHashMap
4.1 JDK1,7是通过分段锁实现线程安全
4.2 JDK1,8通过 synchronized+CAS实现线程安全
- 当产生哈希冲突时,通过synchronized将根节点作为锁,进行线程的同步安全
- 在没有产生哈希冲突时,通过CAS进行无锁化操作,降低synchronized进行线程同步操作所引发的性能下降
面试总结
1.集合中哪些类是线程安全的?
List接口的线程安全实现类:
- Vector :使用 synchronized 同步锁实现线程安全的 List 集合,数据结构是 0bject[]数组;
- Stack: Vector 的子类,是一个实现线程安全并且符合 FIL0 特点的 stack 栈结构,数据结构是 0bject[]数组;
- CopyOnWriteArrayList :使用 ReentrantLock实现写入操作( Write )线程安全的 List 集合,数据结构是 0bject[]数组;
Set接口的线程安全实现类:
- CopyOnWriteArrayset:内部采用CopyOnWriteArrayList实现
Queue 接口的线程安全实现类:
- ConcurrentLinkedQueue:通过CAS 实现线程安全,数据结构是单向链表;
- ArrayBlockingQueue :有界阻塞队列,通过 ReentrantLock 实现读写操作的线程安全和阻塞操作,数据结构是 0bject[]数组;
- LinkedB1ockingQueue :无界阻塞队列,通过 ReentrantLock 实现读写操作的线程安全和阳塞操作,数据结构是双向链表;
Map 接口的线程安全实现类:
- Hashtable :使用 synchronized 同步锁实现线程安全,数据结构是数组+链表;
- ConcurrentHashMap :使用 synchronized 同步锁+ CAS 实现线程安全,数据结构是数组+链表+ 红黑树;
2.什么是阻塞队列
基本介绍
BlockingQueue是并发工具包 java.util.concurrent 中提供的一种线程安全的队列实现,它主要用于生产者-消费者模式中,为多线程应用提供了一种高效和安全的方式来管理共享资源。
BlockingQueue 的主要特点是当队列满时,生产者线程会被阻塞,直到有消费者线程消费了队列中的元素,或者等待超时;
同样,当队列空时,消费者线程会被阻塞,直到有生产者线程添加了新的元素,或者等待超时。
实现类
- BlockingQueue 有几个不同的实现类,包括:
- ArrayBlockingQueue:基于数组的有界阻塞队列。
- LinkedBlockingQueue :基于链表的有界或无界阻塞队列。
- PriorityBlockingQueue :具有优先级的无界阻塞队列,元素需要实现Comparab1e接口。
- DelayQueue :保存 Delayed 类型的元素的无界阻塞队列,元素只有在其延迟过期后才能被消费者线程消费。
- SynchronousQueue :不存储元素的阻塞队列,每个插入操作必须等待另一个线程的相应移除操作,反之亦然。
使用场景
- 生产者-消费者模式:在多线程环境下,生产者生成数据,消费者消费数据,中间使用 BlockingQueue作为缓冲,以实现数据的异步处理和解耦。
- 工作任务队列:在多线程任务调度中,可以使用 BlockingQueue来存放待执行的任务,例如 ThreadPoolExecutor 就使用了Blockingqueue 来管理任务队列。
- 流量控制和限流:在网络通信中,使用BlockingQueue可以限制发送或接收数据的速率,防止数据溢出。
3.什么是CopyOnWriteArrayList?
- CopyOnWriteArrayList 是 java.util.concurrent 并发包提供的线程安全的 List 集合容器,兼顾安全与性能,用于在线程安全场景下取代 Vector和 ArrayList;
- CopyOnWriteArrayList在add()set()、remove()等写入操作中,通过 ReentrantLock加锁,通过COW机制完成数据的写入操作;
- CopyOnWriteArrayList在 get()等读取方法中,没有加锁,允许并发读取,
- Copy0nWriteArrayList 适合读多写少的应用场景;
4.CopyOnWriteArrayList有什么缺点?
内存占用:如果 copyonWriteArrayList 经常要增删改集合中的数据,执行 add()、都需要复制一个新数组,比较耗费内存:set()、remove()方法,每次
数据一致性: copyonwrite 容器只能保证数据的最终一致性,不能保证数据的实时一致性。因为增删改等写入操作的目标是新数组,在没有完成的同时,读取操作的目标仍然是原数组:
5.CopyOnWriteArrayList 实现原理?
- Copy0nWriteArrayList的所有写入操作(add,set 等)都是通过 ReentrantLock 加锁,保证线程安全
- 它的执行流程是:在写入操作时,首先复制出一个新数组,然后在新数组中进行写入操作后,最后用新数组替换原数组,
6.CopyOnWriteArrayList为什么并发安全且性能比Vector好?
- Vector 是增删改查方法都加了synchronized,保证同步,每个方法执行的时候都要去获取锁,性能就会大大降低
- Copy0nWriteArrayList 仅仅是在增删改方法中使用 ReentrantLock 锁,但是读操作不加锁,支持并发读;
7.谈谈ConcurrentHashMap的理解?
- ConcurrentHashMap 是一个线程安全并且性能效率比较高的 Map集合;
- ConcurrentHashMap利用synchronized、 volatile、 CAS实现线程安全,同时减少锁竟争对于性能的影响:
8.ConcurrentHashMap的实现原理?
ConcurrentHashMap在IDK1.7和JDK1.8 采用了两种不同的实现方式:
JDK1,7 :数据结构是 segment 分段数组+链表。它采用分段锁( Segment)对数组进行分割分段,每一把锁只锁容器其中一
部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率,最大并发数量为 Segment 的个数,默认
是16;JDK1.8:数据结构是数组+链表+红黑树,它使用 synchronized 和CAS 来进行并发控制。允许多个线程并发访问 ConcurrentHashMap 。只有在产生哈希冲突后才会锁定产生哈希冲突的链表或红黑二又树的首节点,控制锁定的synchronized范围。只要不产生哈希冲突,就不会产生锁竞争,提高并发效率,最大并发数量是数组的大小;
9.ConcurrentHashMap和HashMap的区别?
ConcurrentHashMap 和HashMap 都是键值对集合,它们主要区别包括:
线程安全
- HashMap :是非线程安全的。在多线程环境下,如果没有 synchronized 块或 ReentrantLock 等外部锁机制,那么在并发写入时可能会导致数据不一致或异常(ConcurrentModificationException);
- ConcurrentHashMap :是线程安全的。它允许多线程安全地进行读写操作,而无需额外的同步代码
数据结构
- HashMap :数据结构使用数组+链表+红黑树,当链表长度超过一定阈值时,链表会转化为红黑树以提高查找效率,
- ConcurrentHashMap :数据结构使用数组+链表+红黑树,使用 synchronized 和CAS 来确保线程安全;
对 nu11 的支持
- HashMap :允许使用 nu11 做 key 或value
- ConcurrentHashMap:不允许nu11键或nu11值:
性能
- 单线程场景中:HashMap 性能好,避免了锁带来的性能开销;ConcurrentHashMap
- 多线程场景中:HashMap 不安全,ConcurrentHashMap 兼顾了安全与性能;
10.ConcurrentHashMap 和Hashtable的区别?
数据结构
- ConcurrentHashMap :数组+链表+红黑树;
- Hashtable :数组+链表;
线程安全方式
- ConcurrentHashMap
- JDK1.8:使用 synchronized 和CAS来进行并发控制。允许多个线程并发访问ConcurrentHashMap,只有在产生哈希冲突后,synchronized 才会锁定产生哈希冲突的链表或红黑二叉树的首节点,控制锁定的范围;
- Hashtable
- 使用 synchronized 来保证线程安全,只允许一个线程访问Hashtable,其它并发线程只能阻塞;