美文网首页
多线程之锁的详解讲解《一》

多线程之锁的详解讲解《一》

作者: 辞令 | 来源:发表于2019-06-20 11:28 被阅读0次

    之前写过线程、线程池,但是并没有深入的讲解,因为线程 多线程是Java中的一个重要环节,不管是使用还是面试,都不可避免用到它,所以会拆开详细的讲解,如有缺陷,请指出,共同进步。
    线程知识巩固第一节
    线程知识巩固第二节
    线程知识巩固第三节
    线程知识巩固第四节
    线程知识巩固第五节
    线程池实现原理ThreadPoolExecutor

    锁在我们的实际开发中用的不是很多,常用的也就Synchronize和volatile。但是后台 JVM 调优的同学会经常碰到。
    锁我在这里大致分了四类:


    锁的四种分类.png

    重量锁:又名互斥锁,它是一种悲观锁。它也就是我们常说的对象锁,使用Synchronize修饰。
    Synchronize的作用:

    1. 访问同步代码阻塞其他线程。
    2. 修改共享变量的能够及时可见性。
    3. 解决重排序问题。

    Synchronize存在的问题:性能不够好

    1. 锁切换会消耗部分性能的:若没有其他线程会竞争同步资源,那么锁的开销是没必要的。
    2. 切换线程的状态是异常消耗性能的:线程的阻塞,挂起和恢复操作都需要深入内核中去完成,代价非常大。
    3. 切换线程会造成线程上次下文的切换,这也是消耗性能的:线程上下文切换开销比切换线程的状态的代价更大。

    自旋锁:又名自适应自旋锁。自旋锁在JDK 1.4.2中就已经引入,只不过默认是关闭的,可以使用-XX:+UseSpinning参数来开启,在JDK 1.6中就已经改为默认开启了。
    自旋锁的优点

    1. 自旋锁是互斥锁的升级版。
    2. 自旋锁在有其他线程竞争同步代码时,我们可以让后面请求锁的那个线程“稍等一会”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁,为了让线程等待,我们只须让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。

    自旋锁的缺点

    1. 自旋等待不能代替阻塞,且先不说对处理器数量的要求,自旋等待本身虽然避免了线程切换的开销。自旋是需要占用处理器时间的,锁被占用的时间越短,自旋等待的效果就会越;反之如果锁被占用的时间越长,那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而会带来性能的浪费。
    2. 自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程了。自旋次数的默认值是10次,用户可以使用参数-XX:PreBlockSpin来更改。

    在JDK 1.6中引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。另一方面,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。有了自适应自旋,随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的状况预测就会越来越准确,虚拟机就会变得越来越“聪明”了。摘自虚拟机中的锁优化简介(适应性自旋/锁粗化/锁削除/轻量级锁/偏向锁)

    轻量级锁 :轻量级锁是JDK 1.6之中加入的新型锁机制。
    轻量级锁的优点:减少无实际竞争情况下,使用重量级锁产生的性能消耗,包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。
    轻量级锁的缺点:同自旋锁相似,如果锁竞争激烈,那么轻量级将很快膨胀为重量级锁,那么维持轻量级锁的过程就成了浪费

    偏向锁 :在没有实际竞争的情况下,还能够针对部分场景继续优化。如果不仅仅没有实际竞争,自始至终,使用锁的线程都只有一个,那么,维护轻量级锁都是浪费的。
    偏向锁的优点:减少无竞争且只有一个线程使用锁的情况下,使用轻量级锁产生的性能消耗。轻量级锁每次申请、释放锁都至少需要一次CAS,但偏向锁只有初始化时需要一次CAS。
    偏向锁的缺点:如果存在其他线程申请锁,那么偏向锁将很快膨胀为轻量级锁。

    锁分为两种特征:锁膨胀 和 锁消除
    锁膨胀:如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,那即使没有线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。 如果虚拟机探测到有这样一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展(膨胀)到整个操作序列的外部(由多次加锁编程只加锁一次)。

    锁消除:锁削除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除(主要判定依据来源于逃逸分析的数据支持,如果判断到一段代码中,在堆上的所有数据都不会逃逸出去被其他线程访问到,那就可以把它们当作栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行)。

    参考 浅谈偏向锁、轻量级锁、重量级锁
    参考 虚拟机中的锁优化简介(适应性自旋/锁粗化/锁削除/轻量级锁/偏向锁)
    参考 android 多线程 — 锁优化

    相关文章

      网友评论

          本文标题:多线程之锁的详解讲解《一》

          本文链接:https://www.haomeiwen.com/subject/rvjdqctx.html