多线程进阶(面试高频)
常见的锁策略:
乐观锁vs悲观锁
指的是一类锁,而不是一把特定的锁
乐观锁:预测接下来锁冲突的概率不大,就可以少做一个工作
悲观锁:预测接下来锁冲突的概率很大,就应该多做一些工作
重量级锁vs轻量级锁
重量级锁:锁的开销比较大
轻量级锁:锁的开销比较小
重量级锁通常是悲观锁,轻量级锁通常是乐观锁
不是100% 一个是锁的实际开销,一个是预计锁的冲突概率
自旋锁vs挂起等待锁
自旋锁:属于是轻量级锁的典型实现
纯用户态实现,比如使用一个while循环,不停检查当前锁是否被释放,如果没被释放,继续循环,如果被释放了,就获取到锁,从而结束循环(忙等,消耗cpu但是换来了更快的响应速度)
挂起等待锁:属于是重量级锁的一种典型实现
借助系统api实现,一旦出现锁竞争,就会在内核中触发一系列动作(比如让线程进入阻塞状态,暂时不参与cpu调度)(线程阻塞、唤醒需要成本)
读写锁
把一把锁分为两个部分,读锁(ReentrantReadWriteLock.ReadLock)和写锁(ReentrantReadWriteLock.WriteLock)
读锁允许多个线程同时获得,而写锁则是互斥锁
读读不互斥,读写互斥,写写互斥
读写锁在读多写少场景中优势大
可重入锁vs不可重入锁
一个线程针对同一把锁,连续加锁两次,不会死锁就是可重入锁,会死锁,就是不可重入锁
公平锁vs非公平锁
多个线程去尝试加同一把锁的时候,只有一个线程能拿到锁,其他线程阻塞等待,一旦第一个线程释放锁之后,接下来是哪个线程拿到锁呢?
公平锁:按先来后到的顺序
非公平锁:剩下线程均等概率竞争锁
操作系统提供的加锁api默认情况属于”非公平锁“
想要实现公平锁,需要引入额外的队列,维护这些线程的加锁顺序
synchronized属于哪种锁呢?
①悲观乐观自适应
②重量轻量自适应
③自旋挂起等待自适应
④不是读写锁
⑤是可重入锁
⑥是非公平锁
初始情况下synchronized会预测当前锁冲突概率不大,以乐观锁、轻量级锁、自旋锁的模式来运行,使用过程中如果发现所冲突的情况比较多,就会升级成悲观锁、重量级锁、挂起等待锁