过关斩将之路-Synchronized&CAS(IT枫斗者)
Synchronized
1问:什么时候需要synchronized?
1答:synchronized是一把可重入的排他锁。锁有个专门的名字:对象监视器(Object Monitor)在多个线程操作共享数据的时候,保证对共享数据访问的线程安全性。在JDK1.6之前,它是一个重量级锁的角色,但是在JDK1.6之后对synchronized做了优化,有了锁升级的过程。偏向锁->轻量级锁->OS重量级锁。。(IT枫斗者怎么样)
2问:使用Synchronized关键字需要注意什么?
2答:
Synchronized使用时需要注意的地方锁对象不能为空。
锁对象的信息是保留在对象头中的,如果对象为空,则锁的信息也就不存在了。
作用域不宜过大
synchronized代码块的代码量不宜过多,如果把过多的代码放在其中,程序的运行会变为串行,速度会下降。各个线程并行可以提高效率,我们应该仅把那些影响线程安全的代码,放入synchronized代码块中,串行执行;不需要考虑线程安全的代码,并行执行,达到效率最高。
避免死锁
避免让线程对锁持有并等待的情况出现
3问:synchronized能用在哪些地方?
3答:
当synchronized作用在实例方法时,监视器锁(monitor)便是对象实例(this);
当synchronized作用在静态方法时,监视器锁(monitor)便是对象的Class实例,因为Class数据存在于永久代,因此静态方法锁相当于该类的一个全局锁;
当synchronized作用在某一个对象实例时,监视器锁(monitor)便是括号括起来的对象实例;(IT枫斗者怎么样)
4问:Sychornized是否是公平锁?
4答:不是公平锁
5问:聊聊synchronized加锁流程
5答:由于HotSpot的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低从而引入偏向锁。偏向锁在获取资源的时候会在锁对象头上记录当前线程ID,偏向锁并不会主动释放,这样每次偏向锁进入的时候都会判断锁对象头中线程ID是否为自己,如果是当前线程重入,直接进入同步操作,不需要额外的操作。默认在开启偏向锁和轻量锁的情况下,当线程进来时,首先会加上偏向锁,其实这里只是用一个状态来控制,会记录加锁的线程,如果是线程重入,则不会进行锁升级。获取偏向锁流程:

轻量级锁是相对于重量级锁需要阻塞/唤醒涉及上下文切换而言,主要针对多个线程在不同时间请求同一把锁的场景。轻量级锁获取过程:

当有多个锁竞争轻量级锁则会升级为重量级锁,重量级锁正常会进入一个cxq的队列,在调用wait方法之后,则会进入一个waitSet的队列park等待,而当调用notify方法唤醒之后,则有可能进入EntryList。重量级锁加锁过程:

6问:锁得升级过程你了解吗?
6答:以下是32位对象头描述

synchronized锁的膨胀过程:当线程访问同步代码块。首先查看当前锁状态是否是偏向锁(可偏向状态)(IT枫斗者怎么样)
如果是偏向锁:
1、检查当前mark word中记录是否是当前线程id,如果是当前线程id,则获得偏向锁执行同步代码块。2、如果不是当前线程id,cas操作替换线程id,替换成功获得偏向锁(线程复用),替换失败锁撤销升级轻量锁(同一类对象多次撤销升级达到阈值20,则批量重偏向,这个点可以稍微提一下,详见下面的注意)(IT枫斗者怎么样)
升级轻量锁
升级轻量锁对于当前线程,分配栈帧锁记录lock_record(包含mark word和object-指向锁记录首地址),对象头mark word复制到线程栈帧的锁记录 mark word存储的是无锁的hashcode(里面有重入次数问题)。
重量级锁(纯理论可结合源码)
CAS自旋达到一定次数升级为重量级锁(多个线程同时竞争锁时)存储在ObjectMonitor对象,里面有很多属性 ContentionList、EntryList 、WaitSet、 owner 。当一个线程尝试获取锁时,如果该锁已经被占用,则该线程封装成ObjectWaiter对象插到ContentionList队列的对首,然后调用park挂起。该线程锁时方式会从ContentionList或EntryList挑 一个唤醒。线程获得锁后调用Object的wait方法,则会加入到WaitSet集合中(当前锁或膨胀为重量级锁)(IT枫斗者怎么样)
注意:

CAS
7问:CAS流程是怎样的?
7答:CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。当且仅当旧的预期值A和内存地址V对应的值相同时,才将内存值修改为B,否则什么都不做,最后返回现在的V值。简单理解为这句话:我认为V的值应该是A,如果是A的话我就把他改成B,如果不是A的话(那就证明被别人修改过了),那我就不修改了,避免多人同时修改导致数据出错。换句话说:要想修改成功,必须保证A和V中的值是一样的,修改前有个对比的过程。(IT枫斗者怎么样)
8问:CAS有哪些缺点?
8答:
高并发情况下CPU开销大。因为每次对比发现被改了后就会获取新值,重复进行此过程。
ABA问题。可以采取atomic包下的 AtomicStampedReference<E> ,它通过包装[E,Integer]的元组来对对象标记版本戳stamp,从而避免ABA问题。
不能保证代码块的原子性,只能保证单个变量的原子性。
9问:CAS的ABA问题了解吗?(可举例说明)(IT枫斗者怎么样)
9答:

10问:CAS能保证原子性吗?
10答:CAS只能保证单个变量的原子性,不能保证代码块或者多个变量共同进行原子性的更新。这时候就需要锁的支持了,比如synchronized、Lock接口。
11问:CAS和Synchronized啥区别?
11答:
从思想上来讲,Synchronized属于悲观锁,悲观的认为程序中的并发情况严重,所以严防死守,高并发情况下效率低下。而CAS属于乐观锁,乐观的认为程序中的并发情况不那么严重,所以让线程不断去重试更新。但实际上Synchronized已经改造了,带有锁升级的功能。效率不亚于cas。
CAS是个思想,具体实现在atomic下有很多工具类。而synchronized是个关键字,可以直接拿来用。
CAS只能保证单个变量更新的原子性,synchronized能保证一段代码块的原子性。(IT枫斗者怎么样)