★
悲观锁:synchronized就是悲观锁,它表示一旦某线程获得锁之后,其他需要锁的线程就挂起等待。
乐观锁:cas操作就是乐观锁,每次不加锁,而是假设没有冲突而去完成某项操作,如果因为冲突失败就重拾,直到成功。
在传统的同步机制中,使用synchronized进行加锁,形成同步代码块,将并行改为串行,从上面分类可以知道,它是悲观的,虽然在1.6之后,进行了锁升级,但是还是存在效率问题,它会阻塞线程,进入block状态,存在线程上下文切换等等问题,
原子操作:原子被任务是不可分割的最小粒子,原子操作,不可被中断的一个或一系列操作。概念说着是简单,但是计算机如何实现原子操作呢?
一,处理器如何实现原子操作
1.使用总线锁住保证原子性:一个处理器读取一个内存时,连接线锁住,不允许其他处理器进行读取该块内存
实现:使用处理器提供的LOCK#信号,当一个处理器在总线上输出此信号,其他处理器的访问内存请求就会被阻塞,这就实现了单独处理器使用内存。
2.使用缓存锁定保证原子性(这是总线锁的优化,有时候只需要锁住某个内存地址):频繁使用的内存会缓存在处理器:L1,L2,L3高速缓存里,大概意思是只允许一个缓存行缓存目标(设置了缓存锁定状态的量)
实现:现在大多数的x86处理器,都支持了CAS的硬件实现,它无需锁住总线,只会阻止其他处理器对相关内存的缓存块的访问。
缓存一致性机制:(使用intel系统中的MESI协议讲述一下原理)
MESI协议是以缓存行(缓存的基本单位,在intel的cpu中,现在基本就是64字节(有点长))的几个状态来命名的:Modified被修改,Exclusive独占,Share共享,Invalid无效的。
该协议要求每个缓存行维护两个状态位,使得每个数据单位可能处于上面四种状态之一,
协议规则:
1.监听: 一个处于M状态的缓存行,必须时刻监听所有试图读取该缓存行对应的主存地址的操作,如果监听到,则必须在此操作执行前把其缓存行中的数据写回CPU。
一个处于S状态的缓存行,必须时刻监听使该缓存行无效或者独享该缓存行的请求,如果监听到,则必须把其缓存行状态设置为I。
一个处于E状态的缓存行,必须时刻监听其他试图读取该缓存行对应的主存地址的操作,如果监听到,则必须把其缓存行状态设置为S。
2.cpu读取数据: 如果其缓存行的状态是I的,则需要从内存中读取,并把自己状态变成S,如果不是I,则可以直接读取缓存中的值,但在此之前,必须要等待其他CPU的监听结果,如其他CPU也有该数据的缓存且状态是M,则需要等待其把缓存更新到内存之后,再读取。
3.cpu写数据: 只有在其缓存行是M或者E的时候才能执行,否则需要发出特殊的RFO指令(Read Or Ownership,这是一种总线事务),通知其他CPU置缓存无效(I),这种情况下会性能开销是相对较大的。在写入完成后,修改其缓存状态为M。
有两种情况下,处理器不会使用缓存锁定
1.当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,处理器会调用总线锁定。
2.有些处理器不支持缓存锁定,
二,java如何实现原子操作
1.使用锁保证原子操作:四种锁机制,除了偏向锁,其他都使用cas来获得锁,使用cas来释放锁。
2.使用循环cas实现原子操作
3.cas实现原子操作的三大问题:aba问题,循环时间长开销大,只能保证一个共享变量的原子操作。
原子操作类:java.util.concurrent.atomic包下,一系列以Atomic开头的包装类,例如:AtomicBoolean,AtmoicInteger...等等,他们分别对应Boolean,Integer类型的原子操作。
原子操作的实现: https://zhuanlan.zhihu.com/p/33445834
缓存一致性: https://blog.csdn.net/qq_21125183/article/details/80848941