volatile ~原子类

目录

1 多线程的可见性

 1.1 什么叫volatile

 1.2 volatile有什么作用, 什么时候用到volatile

2 volatile能解决原子性问题吗

3 原子类解决原子性问题


1 多线程的可见性

 1.1 什么叫volatile

        volatile是一种轻量级的同步机制,与synchronized同步代码块相比,性能更加的好

1.2 volatile有什么作用, 什么时候用到volatile

        vollatile作为一种Java内在的同步机制,可以将我们jvm中主内存的值具有可见性,例如在多线程环境下,定义了一个静态变量a = 0 ,此时会有多个线程会对这个变量进行修改,线程A在自己栈中将值修改后同步到主内存,此时线程B是无法知道主内存的值已经被修改了,但是如果用volatile进行修饰时,线程B就会被强制的重新读取主内存中的值。用一下代码示例:

 大家执行完上面那段代码之后我们是并没有切换到主线程的(因为主线程并不知道副线程已经将值改为了1,从内存中读取的值还是初始值拷进副本的值)

 但是如果你将a用volaitle修饰下会发现,会有截然不同的结果

 这是为什么呢?因为在我们的定义的变量a会存在于jvm方法区里面,而线程之间是一个抢栈式的方式运行,在jvm中每一个线程都会有一个独有的栈,会共用我们堆和方法区中的变量。当每条线程去使用方法区和堆中共享的东西时,会将共享的东西赋值到自己的栈内存(俗称副本)

此时注意:

        第一次访问共享内容的时候加载进入副本,只要栈内存中的值没有修改就会一直使用副本,不会重新去拿。当修改了副本之后,会将我们修改的值同步到共享区域中,之后就会使用我们修改之后的值。

这也是为什么我们没有用volatile关键字之前,主线程所获取到a的值永远是0(当然这个新线程Thread休眠2秒有关,为了直观的看到结果),而使用了volatile之后,即使新线程休眠让主线程拿到a=0的值,当后续a的值改变时主线程也会重新读取到。

2 volatile能解决原子性问题吗

        volatile只能保证可读性,但是并不能保证原子性。代码演示如下

 在多线程的情况下,用了volatile并没有让a的值达到20000.(只有在微乎其微的情况下达到20000)

原因分析:线程A和线程B都共享了方法区的变量a ,此时两个线程的栈中副本值a都为0,当线程抢栈式进行运行时,此时线程A抢夺到了CPU进行了a++操作(比如加到3)此时将值同步到共享方法区中,这时线程B将CPU抢夺,,由于线程B初始值为0,所以会从0开始进行运算,如果此时运算到a=1时,将值同步到内存,此时就会将之前线程A同步到内存的值进行覆盖。造成了值的减少。

3 原子类解决原子性问题

        原子性的解决方案-->原子类(主要针对数据的修改)代码示例:

 解决原因:

        其实a++操作分为3步;

                从主内存中读取值

                进行值运算(加一操作)

                将栈中副本中的新值同步到共享内存中

原子类是将这3部统一了操作,变为了不可分割的部份,这样才避免了线程安全问题。我们可以点进源码去看下:

其中 this:代表当前对象

       valueOffset:内存偏移量

       1:内存偏移多少 var5 :获取当前内存最新的值

this.compareAndSwapInt(var1, var2, var5, var5 + var4));其中这一步是将读取到内存最新的值再于内存中的值进行比较,如果一样就进行操作(为什么有这一步的原因是防止当你从内存中获取最新值之后,又有线程对值进行了修改,所以才会比较),这一步是原子性的,不会被分割。(可能有些同学会问在我比较的时候又有线程对内存中的值进行了修改怎么办呢?其实这种情况是不会发生的,因为这是原子的操作,是Java底层写好的)

        

posted @   小猪不会叫  阅读(23)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
点击右上角即可分享
微信分享提示