各种锁的对比总结

参考

《java并发编程的艺术》
《七周七并发模型》

多线程并发编程中最常用的就是synchronized和volatile两个关键字。

volatile

通常被描述成一个轻量级锁。
用于声明需要在多线程环境中共享的对象。 只能在类实例对象上声明。
功能:
保证当前对象对其它线程可见
禁止代码的重排序

重排序 :代码中上下两段代码不存在依赖关系时,jvm会对代码进行优化排序,排序后的顺序不一定是代码的顺序
内存屏障 : 一组cpu指令,用于实现对内存操作的顺序限制

但是volatile并不能保证线程的安全性

q1:volatile 如何保证可见性
volatile声明的变量在转变成汇编语言在底层执行的时候会多出lock的指令。
该指令会引发两个事情:

  1. 将当前线程的工作内存中的数据写回到主内存中
  2. 写回内存的操作会让其它线程工作内存中缓存了该字段的数据无效

Synchronized

通常被描述成重量级锁,也可称为内置锁
可用于方法,对象,代码段上
功能 :保证当前操作的线程安全性
保证可见性

底层实现:
synchronized是通过对象的monitor的monitorenter和monitorexit来实现对象锁的持有和释放
每个对象上都有一个java 对象头,用来存放当前对象的锁信息,hashcode, 分代年龄,是否偏向锁,锁标志位等信息。
锁按级别从低到高有四种状态:无锁,偏向锁,轻量级锁,重量级锁
如图所示:
这里写图片描述
缺点:

  1. 在获取锁而进入阻塞状态时,是无法中断的
  2. 尝试获取内置锁时,无法设置超时时间
  3. 获得内置锁,必须使用Synchronized块

导致的主要问题就是:
死锁而无法恢复,唯一解决方法是 终止jvm

为了解决内置锁的问题,引入reentrantLock(可重入锁)

可重入锁 ReentrantLock

优势:

  1. 显示的加锁和解锁
        Lock lock = new ReentrantLock();
        lock.lock();
        try{
            ...
        }finally{
            lock.unlock()
        }
  1. 可中断 lock.lockInterruptibly()
  2. 可设置超时时间 lock.tryLock(1000, TimeUnit.MILLISECONDS)

偏向锁

是指被同一线程重复获取的锁
优点是:
进入和退出锁的时候不需要cas操作来加锁,解锁。效率更快

这里写图片描述

如何关闭偏向锁
java1.6, 1.7是默认启用的,
jvm参数: -XX:BiasedLockingStartupDelay = 0

轻量级锁的获取及膨胀流程
这里写图片描述

锁的优缺点对比

优点 缺点 适用场景
偏向锁 加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳米级的差距 如果线程之间存在锁竞争会带来额外的锁撤销的消耗 适用于只有一个线程访问同步块的情况
轻量级锁 竞争的线程不会阻塞,提高线程的响应速度 如果始终得不到锁竞争的线程,使用自旋回消耗cpu 追求响应时间,同步块执行速度非常快
重量级锁 线程竞争不使用自旋,不会消耗cpu 线程阻塞,响应时间缓慢 追求吞吐量,同步执行时间较长

三者之间的区分,如果只有一个线程重复进入同步块,那是偏向锁,如果多个线程进入同步块,不会阻塞,那是轻量级锁,会阻塞,是重量级锁

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章