• 6、锁优化


      高效并发是jdk1.5到1.6的一个重要改进,hotspot虚拟机开发团队在这个版本上花费了大量精力去实现各种锁优化技术,例如:适应性自旋、锁消除、锁粗化、轻量级锁跟偏向锁等,这些技术都是为了在线程间更高效的共享数据,以解决竞争问题,从而提高程序执行效率。
    自旋锁与自适应自旋:
      虚拟机开发团队注意到,在许多应用上共享数据的锁定状态只会持续很短的时间,为了这段时间去挂起跟恢复线程并不值得(线程的内核态跟用户态转换消耗较高),如果物理机有一个以上的处理器,我们可以让后面请求锁的线程不要放弃cpu的执行时间,而是“稍等一下”,看看持有锁的线程是否会很快释放锁;这个“等一下”一般会实现为一个for循环(自旋),这就是所谓的自旋锁。
      但for循环的时候也会消耗cpu性能!!如果前一个线程很快释放了锁还好,如果执行较慢,在等待的线程一直自旋,对cpu也是一种很大的消耗,因此自旋要有条件限制,一般默认次数为10次,自旋10次无法获取锁,则挂起线程。因为等待时间不可控的情况,jdk1.6引入了自适应的自旋锁。就是jdk会根据自己等待的“经验”来决定自旋时间;如果之前的等待都成功了,则认为本次执行也很可能成功,等待时间会适当延长。
    锁消除:
      虚拟机即时编译器在运行的时候,会对一些代码上要求同步,但被检测到不可能存在同步的代码进行锁消除。锁消除的主要判断依据来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其它线程访问到,那就可以把它们当做栈上的数据对待,认为他们是线程私有的,同步加锁无需进行。
    例如:
    public String concatString(String s1, String s2, String s3){
      StringBuffer sb = new StringBuffer();
      sb.append(s1);
      sb.append(s2);
      sb.append(s3);
      return sb.toString();
    }
      StringBuffer的append是同步的,但经过分析,我们发现它的动态作用域被限制在concatString方法中,相关变量也都在线程执行的jvm栈中,其它线程无法获取,因此,虽然这里有锁,但可以安全的消除掉,即时编译器编译之后,这段代码就会忽略掉所有的同步而直接执行了。
    锁粗化:
      写代码的时候,我们总是要把同步的范围设置的尽量小,以使得需要同步的代码量尽量变小,执行速度快,而让其它线程尽快拿到锁。
    但如果一系列的操作都是对同一个对象反复加锁跟解锁,甚至加锁操作在一个循环中,会导致即使没有竞争,也会因为频繁加解锁而产生不必要的性能损耗。例如上例的concatString方法有三个append都是对同一对象加锁,其实只需要一次就够了;如果jvm探测到这样的代码,会把加锁同步范围扩展(粗化)到真个操作序列的外部(当然,只是举例说明,实际这里最终会因为没有竞争而被消除锁)。
    轻量级锁:
      轻量级锁不是用来代替重量级锁的,它的本意实在没有多余线程竞争的前提下,减少传统的重量级锁因为使用操作系统互斥量而产生的性能消耗。
    jvm中java对象的对象头包含4部分:hashCode,gc分代,锁标志位,固定位0。
      轻量级锁就是采用一种乐观锁的机制,对对象的锁标志位进行“抢占”,成功,则将锁标志位设置为一个指向线程执行栈的指针(所指向的栈帧存放原来锁标志位的内容),失败则检查是否当前线程持有该对象锁,是则继续执行,否则说明发生了多线程锁竞争,升级为正常的重量级锁。
      轻量级锁采用cas操作,避免了互斥量的开销,相对来说比较轻便,但如果竞争较为频繁的话,轻量级锁不但升级为了普通的重量级锁,而且还额外多了cas的操作,会导致轻量级锁比重量级锁更慢。
    偏向锁:
      偏向锁的目的是消除代码在无竞争的情况下的同步操作,进一步提高程序的性能。如果说轻量级锁是在无竞争的情况下使用cas操作去代替互斥量,那偏向锁就是在无竞争的情况下,干脆把整个同步都消除掉,连cas操作都不作了。
      偏向锁的“偏”,就是偏心的偏,意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中该锁没有被其它线程获取,则持有偏向锁的线程将永远不需要再进行同步。实现原理跟轻量级锁一样,也是通过修改对象头信息中的锁标志位进行的。
      当有另一个线程尝试获取这个锁时,偏向模式就宣告结束,根据锁对象目前是否处于被锁定状态,撤销偏向后恢复到未锁定状态,或者轻量级锁定状态。同轻量级锁一样,偏向锁也是一个带有效益权衡性质的优化,如果程序中大多数的锁总是存在多个线程的竞争访问,那偏向锁就是多余的,这时候禁用掉偏向反而会提升性能。
     

  • 相关阅读:
    wf(三)
    WF(二)
    WF4.0入门(一)
    枚举
    函数和立即函数
    对象字面量
    Break和continue语句
    对象
    循环语句
    条件分支语句(SWICH语句)
  • 原文地址:https://www.cnblogs.com/nevermorewang/p/9682412.html
Copyright © 2020-2023  润新知