JVM高级特性与最佳实战(六)————线程安全与锁优化

引言

今天是我学习《深入理解java虚拟机》最后一天了,我希望能把最后一章讲解的清晰。根据我的开发经验讲出自己的解释,给大家一个直观的理解。学完这个以后,下一步我打算把设计模式学完。《head first 设计模式》中我选了5篇设计模式,不再多学了,设计模式学那么多也没有,关键是练练思维。

线程安全

  • 线程安全的实现方法
    (1)互斥同步,也叫阻塞同步,是一种悲观的并发策略。 互斥是实现同步的一种手段,互斥是方法,同步是目的。常用的互斥手段有:临界区(critical section),互斥量(mutex),信号量(semaphore)。同时还有基本的syncronnized 和JUC的ReentrantLock。这一块我会在Java高并发程序设计着重讲解,请大家去看另外的板块内容。
    (2)非阻塞同步,是一种基于冲突检测的乐观并发策略。 主要的思想是:先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就采取其他的补偿措施,比如一直retry,直到成功为止。常用的方式有CAS操作(比较并交换)。
    那么什么是CAS呢?
    CAS指令通常需要有三个操作数,分别是内存位置V,旧的预期值A,新值B。指令执行时,当且仅当V符合旧的预期值A时,才能更新为新值B,否则不更新。无论是否更新了V的值,都会返回V的旧值。
    但是CAS有一个ABA的问题,就是在赋值的过程中,检查他仍为A,就代表他没有被改过么?如果在这段期间他的值曾经被改为B又被改为A,实际上数据曾经发生过变化。但是仍然赋值成功了。JUC提供了一个AtomicStampedReference通过控制版本来保证CAS的正确性。不过目前这个问题比较鸡肋,数据就算发生变化,对最终数据没有影响。
    (3)无同步方案,要保证线程安全,不是一定要进行同步,两者没有必然因果关系。同步只是保证共享数据争用时的正确性的手段。
    a 纯代码,不依赖存储在堆上的数据和公共的系统资源,用到的状态量都由参数传入,不调用非可重入方法。
    b 线程本地存储,如果一个变量要被某个线程独享,可以用threadlocal来实现线程本地存储的功能。
  • 锁优化
    (1)自旋锁(自适应锁):
    共享数据的锁定状态只会持续很短的时间,这时候去挂起和等待线程并不值得,这时候只需要让另外一个线程进行忙循环,这就是自旋锁。jdk1.4中就已经有了这个锁,但是直到jdk1.6才默认开启,并且引入了自适应自旋锁,意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的持有者的状态决定。
    (2)锁消除
    锁消除是指虚拟机即时编译时,对一些代码上要求同步,但是被虚拟机检测到不可能存在数据共享进行的锁消除。你比如在一段纯代码上加锁,很有可能被虚拟机进行锁的消除操作。 且锁消除的主要判定依据是逃逸分析的数据支持。
    (3)锁粗化
    如果一系列的连续操作,都要对同一个对象反复加锁,虚拟机将会把加锁的同步范围扩大到整个操作序列的外部。几个连续的StringBuilder.append()就会被粗化到外部。这样只需要加一次锁就够了。
    (4)轻量级锁
    这个必须讲一下虚拟机的对象头组成,第一部分存储对象自身的运行时数据,叫做Markword。另一部分用于存储指向方法区对象类型数据的指针。
    轻量级锁通常会做一下4个步骤:
    a.在栈中分配一块空间用来做一份对象头部mark word的拷贝,在mark word中将对象锁的二进制位设置为“未锁定”(在32位的JVM中通常有2位用于存储锁标记,未锁定的标记为01),这个动作是方便等到释放锁的时候将这份数据拷贝到对象头部。
    b. 通过CAS尝试将头部的二进制位修改为“线程私有栈中对mark区域拷贝存放的地址”,如果成功,则会将最后2位设置为00,代表已经被轻量级锁锁住了。
    c. 如果没有成功,则判定对象头部是否已经指向了当前线程所在的栈当中,如果成立则代表当前线程已经持有锁,可以继续执行。
    d. 如果没有持有,则说明有多个线程在争用,那么此时会将锁升级为悲观锁,线程进入BLOCKED状态。

    (5)偏向锁
    其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间。 如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLocking,偏向锁在锁竞争激烈的场合没有太强的优化效果,因为大量的竞争会导致持有锁的线程不停地切换,锁也很难保持在偏向模式,此时,使用偏向锁不仅得不到性能的优化,反而有可能降低系统的性能,因此,在激烈竞争的场合,可以尝试使用-XX:-UseBiastedLocking参数禁用偏向锁。

作者:select you from me
来源:CSDN
转载请联系作者获得授权并注明出处。

你可能感兴趣的:(JVM高级特性与最佳实战)