锁主要存在四种状态,无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。只可升级不可降级,为了提高获得锁和释放锁的效率。
偏向锁
会偏向于第一个获得它的线程,在接下来的执行中,该锁没有被其他线程获取,那么持有偏向锁的线程就不需要同步。
因为多线程情况下申请锁的线程很可能都不同,所以在锁竞争比较激烈的场合就会失效了。失效后并不会立即膨胀为重量级锁,而是先升级为轻量级锁。
轻量级锁(JDK1.6之后引入)
不是为了代替重量级锁,本意是在没有多线程竞争下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗,因为使用轻量级锁时不需要申请互斥量,另外轻量级锁的加锁和解锁都用到了CAS操作。
一般情况下对于大部分锁,在整个同步周期内都是不存在竞争的。 如果没有竞争,轻量级锁通过使用CAS操作避免了使用互斥操作的开销。 但是如果存在锁竞争,除了互斥量开销外,还会额外发生CAS操作,这样会比重量级锁更慢。锁竞争会膨胀为重量级锁。
自旋锁和自适应自旋
轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,会进行名为自旋锁
的优化手段。
互斥同步对性能影响最大的影响就是阻塞的实现,因为挂起、恢复线程的操作都需要转入内核态中完成(用户态-内核态会耗费时间)。
一般线程持有锁的时间都不是太长,为了这一点时间而去挂起/恢复线程是得不偿失的,为了让一个线程等待,我们只需要让一个线程执行一个忙循环(自旋)即可。
百度百科-自旋锁
何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
在JDK1.6之后引入了自适应的自旋锁。对于之前的改进就是:自旋的时间不再固定,而是和前一次同一个锁上的自旋时间以及锁的拥有者来决定。
锁消除
在虚拟机即使编译器在运行时,如果检测到那些共享数据不可能存在竞争,那么就执行锁消除。锁消除可以节省毫无意义的请求锁的时间。
锁粗化
平常在写代码的时候,都要求将同步块的区域限制的很小,只在共享数据的实际作用域才进行同步。但是如果一系列的连续操作都对同一对象反复加锁和解锁,会有不必要的性能消耗。
参考资料:https://github.com/Snailclimb/JavaGuide/blob/master/Java相关/synchronized.md