笔记(二)--synchronized锁的演变

各种常用的锁:

重入锁(递归锁):递归调用自己加锁方法的时候,可以再次进入该方法,优点就是相 同线程不需要在等待锁,而是可以直接进行相应操作。

不可重入锁:即使是本身,也只能进入一次方法。
悲观锁:对每次线程都认为会进行写操作,每次只让一个线程进入。(认为读少写多(synchronized实现))

乐观锁:都不加锁,在更新的时候判断是否发生改变。!CAS实现的,它是一种更新原子操作,比较当前值是否和传入值一致,一样就更新。

公平锁:大家排好队,一个一个来获得锁,但是需要排队机制,而且效率不高。

非公平锁:谁抢到谁的,但是可能有些线程一直得不到锁。效率相对高 (synchronized)。

java线程阻塞的代价:(synchronized演变的原因)

java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在户态与核心态之间切换,这种切换会消耗大量的系统资源,
因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时
的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。

如果线程状态切换是一个高频操作时,这将会消耗很多CPU处理时间;如果对于那些需要同步的简单的代码块,获取锁挂起操作消耗的时间比用户代码执行的时间还要长,这种同步策略显然非常糟糕的。
synchronized会导致争用不到锁的线程进入阻塞状态,所以说它是java语言中一个重量级的同步操作,被称为重量级锁,为了缓解上述性能问题,
JVM从1.5开始,引入了轻量锁与偏向锁,默认启用了自旋锁,他们都属于乐观锁。

markword:

markword是java对象数据结构中的一部分,用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit。

synchronized锁在1.6后的演变:

JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。
锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级;
这种策略是为了提高获得锁和释放锁的效率。


偏向锁:同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,这种情况下,就会给线程加一个偏向锁。会消除同步,效率加快。
始终只有一个线程在执行同步块,在它没有执行完释放锁之前,没有其它线程去执行同步块,在锁无竞争的情况下使用,(适用于无锁竞争的情况)
(缺点:一旦有了竞争就升级为轻量级锁,升级为轻量级锁的时候需要撤销偏向锁,撤销偏向锁的时候会导致stop the word操作)


轻量级锁:它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗,因为使用轻量级锁时,不需要申请互斥量。
另外,轻量级锁的加锁和解锁都用到了CAS操作。(如果轻量级锁自旋到达阈值后,没有获取到锁,就会膨胀为重量级锁)

自旋锁:如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(去执行一个空循环),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。(适用于竞争不激烈的情况,在轻量级使用)自旋会一直消耗cpu资源。

猜你喜欢

转载自blog.csdn.net/njh1147394013/article/details/112302017