建议先解Java的CAS。
一、Java中的锁分为两种:轻量级锁,重量级锁
轻量级锁,基z于CAS:
轻量级锁常见的两个问题:
1.ABA问题:
简单解释就是有一个基本类型a = 8,一个线程把他修改为a = 1,另一个线程有把他修改为a = 8,虽然前后都是8,但是中间经历了种种变化,也就是ABA问题。然而对于基本类型的变量来说这样的修改是无伤大雅的。但对于引用类型,比如Person p = new Person(),p的这个指针,也就是引用地址,如果中间发生变更又被线程改为原来的值,那么结果可想而知,肯能定会有问题的。所以关于ABA问题,重点要看引用类型的属性。解决办法就是给这个属性加版本号或者boolean类型的标记,一旦前后不一致就不做修改操作。
2.如何保证轻量级锁的原子性:
由java底层代码c++实现,jvm对线程修改操作加锁,是将线程读操作和写操作作为一个原子性的操作。这里底层加的锁的量级肯定是低于轻量级锁的,是jvm底层保证的。
重量级锁:synchronized
重量级锁是由操作系统管理的锁,其实说重量级锁其实说的就是锁升级的过程。Java中锁的状态有四种:
1.无锁状态
2.偏向锁
3.自旋锁
4.重量级锁
锁升级的过程:
Java中默认每个对象都关联一个同步监视器(monitor),通俗一点就是每个对象都可作为一个锁。不加锁的时候,就是无锁状态 ,加锁之后有一个线程访问时则升级为偏向锁,就是偏向第一个访问的线程。随着访问的线程变多,就升级为自旋锁。说下自旋锁,在Java最初的版本中,自旋锁中有两个重要的参数,一个就是自旋次数,默认是10,当一个线程在等锁时会不停的自旋(底层就是一个while循环),当自旋的线程达到CPU核数的1/2时,就会升级为重量级锁----synchronized。但在jdk1.6之后改为自适应自旋,自旋次数是根据程序历史数据等等判断自旋的数量。
自旋就像你去上厕所,发现里面有人,然后你就提着裤子转圈,当上厕所的人多了,一对人在外面提裤子转,当人数变得多时(肯定有一个上限),就得大管家(操作系统)出面,掏出它那令人畏惧的大锁Synchronized,来管理这些上厕所的人(线程)了。
为什么会有两种锁,既然有轻重之分,其实是在效率上要考虑的问题,你想啊,jvm能自己即时处理的问题,再麻烦操作系统,找操作系统申请重量级锁是花费时间的。这是我的简介。
两种锁的选择:
执行时间长的,线程多的情况下---->重量级锁(悲观锁)
执行时间短,线程少的情况下---->自旋锁(乐观锁)
悲观锁比较小心眼,怕你修改数据,二话不说,上来就上锁。乐观锁,默认不会修改数据,先不加锁。