集合相关
- 集合相关异常,以及使用各集合间的优缺点?
- 临时解决方案:百度、咨询同事、快速界解决问题,避免影响项目进度;
- 解决后的问题反思:
ArrayList
是单线程,非线程安全,需要手动加锁实现同步lock或sychornized;因为代码不方便重用,CopyOnWriteArrayList
,它底层是:写加锁,ReentrantLock
,读不加锁;ReentrantLock
,基于AQS
同步,它的公平锁和非公平锁
,AQS
的基础又是CAS
。区别ReentrantLock和sychornized
,和jdk关键字优化有关。
CopyOnWriteArrayList
底层数组由volatile
修饰,保证内存可见性
,这里涉及到重排序
,happen-before
,CAS
,内存屏障
。注意原子类型,底层就是volatile
修饰的。CAS
类似数据库中的乐观锁
。
CopyOnWriteArrayList
的使用场景:读多写少的场景,避免大量临时数组产生
(因为每次写的时候,需要两个数组,写操作并发高,导致新生代产生大量临时数组,需要大量GC)、黑/白名单
(利用分布式Nginx实现ip黑名单,限流,负载均衡,反爬虫),同包下还有ConcurrentHashMap
,7与8的Hashmap和ConcurrentHashMap区别
。
保证思路连续性
- 一些常见问题:ArrayList的坑、集合优缺点、集合中常见异常、ArrayList能不能在多线程中使用
ConcurrentModificationException并发修改异常
,产生于iterator迭代遍历ArrayList,删除一个元素,产生异常,for/foreach也是,expectedModCount
表示修改次数期望值,初始值与modCount
相等,但是修改集合成员时,modCount
会变化,当两者不相等是,抛出这个异常,所以需要移除时不能使用list.remove(xxx),而是使用iterator.remove(),因为这个方法会设置那两个参数相等。
说出在什么场景下出现异常
(发现问题能力)
、说出异常原因(分析问题能力)
、说出解决方案(解决问题能力)
、对一个小问题的扩展和延伸(总结问题能力)
示例:发现问题——ArrayList遍历出现异常、分析问题——找到源码原因、解决问题——使用迭代器remove方法可以避免、总结问题——多线程场景下迭代器remove方法产生问题引出并发容器
引出CopyOnWriteArrayList,在查看mysql驱动程序类的时候Driver中发现的
- 项目中的例子:
- CopyOnWriteArrayList原理
- java.util.concurrent包、字面意思write写操作需要copy、任何可变操作(add,set,remove等操作)伴随复制动作
- 源码浅析
添加元素——add(E e)
lock.lock(); //加锁,保证线程安全
Arrays.copyOf(); //拷贝出新数组 private volatile transient Object[] array;
lock.unlock(); //finally中解锁,防止出现异常导致锁不释放
/**
* 大致流程都是 先lock一下保证线程安全,拷贝出新数组,操作,修改原数组引用指向新数组,unlock解锁
* 高并发读请求,走的是旧数组。
*/
- CopyOnWriteArrayList面试关键点
- 写时复制机制
- 写操作加锁/解锁
- 读操作不需要几所
- 体现读写分离的思想
- 体现最终一致性思想
- 在JDBC驱动程序源码中广泛使用
- 使用场景:读多写少场景——黑名单、白名单、商品类目
-
延伸至 volatile关键字
volatile是保证线程内存可见性 ,区别于加锁。
volatile通过JVM底层一些指令,例如:happen-before规则,内存屏障等技术,保证cpu0放入到memory内存中,执行jvm中指令,强制硬件层面上,其他cpu的cache缓存为无效状态,强制从memory中获取新值,放到各自的缓存中进行计算。"大致是清其他cpu缓存,再重新拿缓存"
-
延伸至 CAS原子类型操作
CAS(Compare and Swap)
原子类型:保证操作都是线程安全的。
例如:AtomicInteger 底层有一个 private volatile int value; //实际就是int + volatile修饰
- CAS有三个操作数
内存值V
、旧的预期值A
、要修改的新值B
当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
- 延伸至 乐观锁
- 延伸至 锁
Synchronized是依赖于JVM实现的(操作系统实现)、ReentrantLock是JDK实现的(开发者实现)、两者性能相当
- 常见的ReentrantLock
重入锁
:同一时间,只有一个线程可以使用临界资源;公平锁
是若是同一线程竞争资源,是可以加上锁的,先来后到;非公平锁
,后续线程没有按照队列形式来拿到锁。
在多线程高并发环境下,多线程竞争锁,有一个竞争成功加上锁,成功的线程直接执行方法逻辑;
加锁失败,生成阻塞队列
,存放所有等待线程,等待获取释放锁后的使用权;当前一个使用者释放锁, 队列会唤醒一个线程去竞争资源加锁,以达到线程安全的效果;成功就加锁,失败就再回到阻塞队列。
- sychornized 和 volatile关键字
volatile是轻量级的synchronized。锁提供了两种特性,互斥和可见性。因此在保证多线程读写时数据的一致性,可以使用同步即synchronized关键字和可见性即volatile。
1.volatile
可见性的意思即当一个线程修改了共享变量时,另一个线程也能读取到这个最新的数值。
对于volatile修饰的变量在进行写操作时,处理器不会直接和内存通信,会先写入到缓存中。在这个写入缓存的过程中,用到缓存一致性确保修改的原子性。当缓存回写到内存中时会导致其他处理器对此变量的缓存无效,会重新读取此变量的值。
2.synchronized和volatile的区别
1)volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
2)volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
3)volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性.
4)volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.