线程安全与锁优化-《深入理解java虚拟机》读书笔记

上一篇:java内存模型——《深入理解java虚拟机》读书笔记

一、线程安全的实现方法?

  1. 互斥同步
  • synchronized关键字,块结构同步语法。被其修饰的同步块对同一个线程来讲是可重入的,所以同一线程反复进入同步块也不会出现自己把自己锁死的情况。被其修饰的同步块在持有锁的线程执行完毕并释放锁之前,会无条件阻塞后面线程的进入。synchronized是一种重量级锁
  • JUC包提供了Lock接口,在类库层面实现同步,重入锁和读写重入锁是Lock最常见的一种实现,以重入锁ReentrantLock为例,相比于synchronized增加了一些高级功能。
    • 等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。此特性对处理执行时间比较长的同步块很有帮助
    • 公平锁,synchronized是非公平锁,ReentrantLock默认也是非公平的,但提供了公平锁的实现。不过一旦使用了公平锁,ReentrantLock是的性能会急剧下降,明显影响吞吐量
    • 绑定多个条件:同一个ReentrantLock对象可以同时绑定多个Condition对象。synchronized中,锁对象的wait()跟它的notify()或者notifyAll()方法配合可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外添加一个锁;而ReentrantLock则无须这样做,多次调用newCondition()方法即可。
  • lock接口和synchronized两个性能对比如何呢?
    以ReentrantLock与synchronized对比为例,在JDK5之前,多线程环境下synchronized的吞吐量下降得非常严重,而ReentrantLock则能基本保持在同一个相对稳定的水平上。JDK6之后,加入了大量针对synchronized锁的优化措施synchronized与ReentrantLock的性能基本上能够持平,所以性能已经不再是选择synchronized或者ReentrantLock的决定因素。
  • 虽然ReentrantLock是Synchronized的超集,性能上也不弱于后者,在两者都满足需要时,基于以下理由,书中建议优先使用synchronized
    • synchronized是java语法层面的同步,足够清晰,也足够简单,只需要基础的同步功能时,推荐使用
    • Lock应该确保在finally块种释放锁,否则一旦受同步保护的代码块抛出异常,则有可能永远不会释放持有的锁,而synchronized则可以由java虚拟机来来确保即使出现异常,锁也能被自动释放
    • 从长远来看,Java虚拟机更容易针对synchronized来进行优化,因为Java虚拟机可以在线程和对象的元数据中记录synchronized中锁的相关信息,而使用J.U.C中的Lock的话,Java虚拟机是很难得知具体哪些锁对象是由特定线程锁持有的
  1. 非阻塞同步

    基于冲突检测的 乐观并发策略。通俗地说就是不管风险,先进行操作,如果没有其他线程争用共享数据,那操作就直接成功了;如果共享的数据的确被争用,产生了冲突,那再进行其他的补偿措施,最常用的补偿措施是不断地重试,直到出现没有竞争的共享数据为止。
    操作和冲突检测这两个步骤需要具备原子性,通过硬件来保证。硬件保证某些从语义上看起来需要多次操作的行为可以只通过一条处理器指令就能完成,这类指令有很多。这里以比较并交换(Compare-and-Swap,下文称CAS)为例。
    CAS指令需要有三个操作数,分别是内存位置(在Java中可以简单地理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和准备设置的新值(用B表示)。CAS指令执行时,当且仅当V符合A时,处理器才会用B更新V的值,否则它就不执行更新。但是,不管是否更新了V的值,都会返回V的旧值,上述的处理过程是一个原子操作,执行期间不会被其他线程中断。
    CAS有个典型的ABA问题:如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然为A值,那就能说明它的值没有被其他线程改变过了吗?这是不能的,因为如果在这段期间它的值曾经被改成B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA问题”。
    大部分情况下ABA问题不会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更为高效。

  2. 无同步方案

  • 可重入代码:这种代码又称纯代码(Pure Code),是指可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误,也不会对结果有所影响。
  • 线程本地存储:可以通过java.lang.ThreadLocal类来实现线程本地存储的功能
    二、锁优化技术有哪些?
  • 自旋锁和自适应自旋
  • 锁消除
  • 锁粗化
  • 轻量级锁
  • 偏向锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值