zoukankan      html  css  js  c++  java
  • Java中的多线程安全--volatile,Atomic系列类与锁

    多线程情景分析

    1. 随机性

      默认情况下,CPU时间片抢占式调度,无法预测哪个线程会在什么时间拿到执行权

    2. 可见性

      共享数据的修改并不是所有线程都能看见。

      不同线程共享静态成员变量,对于方法区内静态区的变量,并不是直接取用。为了使用共享区数据,Java采用的是1+2的方法:

      1. 如果是第一次访问,在自己线程的栈上创建该变量的副本,只要不修改,就不会再重新获取

      2. 如果修改,就更新自己栈上副本的值,立刻同步到方法区中(共享区域),后续使用修改后的副本

      image-20210424102545770

      以上代码执行结果是无限循环。新线程虽有重新赋值,但是在等待过程中,主线程已经获取到了原值(0),并生成了副本,自己又没有修改,也就不会再重新去拿了,任你新线程如何修改,即便a已经被更新,对主线程而言,a仍旧是0。

    3. 有序性

      JVM翻译Java代码,对于没有上下文依赖的代码,可能会调整代码的顺序,此所谓指令重排。在单线程情况下,不会有影响,但是多线程时,就会影响到执行结果。但说到底,这一问题还是多线程的随机性造成的。

      image-20210424103639344

    4. 原子性

      image-20210424104358166

      以上代码中,a并不一定会被累计到20000,原因就在于t1和t2获取的a并不一定是最新的。何以解决?加上volatile可以吗?

      volatile

      volatile具体做了什么?其实就是改变了使用共享数据的逻辑,由第一次访问获取更新改为:只要使用共享变量,强制获取最新变量,并重写副本。另外的作用就是让JVM不要给涉及这个变量的语句指令重排。volatile意为变动的,不稳定的。正如其字面含义,被volatile修饰的变量,总是处于变化之中,每次获取都要用最新的值。

      但是,注意volatile只是改变了获取共享区数据的方式,但是并不涉及修改,也就是说获取之后还是照旧去修改本地副本并更新共享区数据,随机性问题并没有得以解决。我们真正需要做到的是让使用共享区数据的几个步骤变为一个整体,中间操作过程中,不要有其它线程插入。由此引入了原子类,即一批Atomic开头的Java类。

      原子类

      image-20210424110041090

      原子类累加时,发生了什么?

      image-20210424110232869

      可以看出,累加时先要获取主内存中的值,之后再反复确定获取的值与主内存中的值是否一致,不一致就继续获取,一致后执行累加,返回结果。在比较过程中,不会再有其它线程插入(可以理解为还是有一个悲观锁),确保比较过程不会出错。这就是所谓CAS,这是乐观锁的一种实现方式。

    1. 既然又有乐观锁,又有悲观锁,且乐观锁底层还是要依靠悲观锁的机制,为什么又要有两样东西呢?

      我们来考虑三个窗口买票的问题。三个窗口买票,不加处理,可能会卖重票与卖负票。其原因有三:多线程环境,数据共享,操作共享数据的过程中可能被其它线程抢到。 一二不可避免,能着手解决我呢提的部分只有三,即将操作过程隔离,一次只能有一个线程操作,如同建上围墙,只留一扇小门,一次只是从门放入一人。Lock接口的实现方式也是一样。只是synchronized跟接近于面向过程编程,而Lock接口则是面向对象编程,也就能实现一些更为精细化的操作,二者效率在JDK1.5后已无不同。

      上面已经提到了原子类,如果把票替换为原子类实现(乐观锁),能解决卖重票和负票的问题吗?答案是不能,原子类能保证原子性的范围,只限于修改,即执行getAndIncrement方法期间,操作逻辑的其它部分照样。由此可见,乐观锁与悲观锁的使用范围并不一样。试比较二者:

      1. 乐观锁

        只针对值的修改,只在修改处加校验,操作的其它大断逻辑它不管。

        适用于多查少改。如果改动很多,用乐观锁效率反而变低,因为CAS循环次数变多。

      2. 悲观锁

        针对打大段的上下文关联的逻辑,把大段逻辑变为原子性。

        适用于多改少查

      image-20210424111114383 

     参考资料:

    https://www.bilibili.com/video/BV1sE411c7JC?p=8&spm_id_from=pageDriver

  • 相关阅读:
    js 变速动画函数
    js 获取滚动条事件
    js 获取任意一个元素的任意一个样式属性的值
    js 三大事件(鼠标.键盘.浏览器)
    关于数组的一些方法
    mvc获取时间戳
    html5响应式设置<meta>
    jq遍历url判断是否为当前页面然后给导航上色
    mvc正则@符号js报错解决办法
    无法在提交表单前通过ajax验证解决办法
  • 原文地址:https://www.cnblogs.com/yifeixu/p/14696549.html
Copyright © 2011-2022 走看看