摘要:的缺点频繁刷新主内存中变量,可能会造成性能瓶颈不具备操作的原子性,不适合在对该变量的写操作依赖于变量本身自己。
作者:毕来生1. 什么是JUC?
微信:878799579
JUC全称 java.util.concurrent 是在并发编程中很常用的实用工具类
2.Volatile关键字1、如果一个变量被volatile关键字修饰,那么这个变量对所有线程都是可见的。
2、如果某条线程修改了被Volatile修饰的这个变量值,修改后的值对于其他线程来时是立即可见的。
3、并不是经过Volatile修饰过的变量在多线程下就是安全的
4、多线程间可以使用SynchronousQueue或者Exchanger进行数据之间传递
内存可见性(Memory Visibility)是指当某个线程正在使用对象状态 而另一个线程在同时修改该状态,需要确保当一个线程修改了对象 状态后,其他线程能够看到发生的状态变化。
可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。
原理同CAS原理相同,不懂的同学可以自行百度,附上一张CAS演示图供大家参考
通过线程来修改变量count的值,使用Volatile关键字修饰和不使用Volatile修饰count变量结果对比。
首先我们来看一下通过内部类实现Runnable,变量使用Volatile关键字修饰演示以及结果
package org.bilaisheng.juc; /** * @Author: bilaisheng * @Wechat: 878799579 * @Date: 2019/1/1 16:29 * @Todo: 通过内部类实现Runnable,变量使用Volatile关键字修饰演示 * @Version : JDK11 , IDEA2018 */ public class NoVolatileTest{ public static void main(String[] args) { NoVolatileThread noVolatileThread = new NoVolatileThread(); new Thread(noVolatileThread).start(); while (true){ if(noVolatileThread.isFlag()){ System.out.println("flag 此时为true !"); break; } } } } class NoVolatileThread implements Runnable{ private boolean flag = false; @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println(Thread.currentThread().getName() + " flag = " + flag); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
运行结果如下图所示:
接下来我们来看一下通过内部类实现Runnable,变量不使用Volatile关键字修饰演示以及结果
package org.bilaisheng.juc; /** * @Author: bilaisheng * @Wechat: 878799579 * @Date: 2019/1/1 16:53 * @Todo: 通过内部类实现Runnable,变量使用Volatile关键字修饰演示 * @Version : JDK11 , IDEA2018 */ public class VolatileTest{ public static void main(String[] args) { VolatileThread volatileThread = new VolatileThread(); new Thread(volatileThread).start(); while (true){ // if的判断volatile保证当时确实正确,然后线程a可能处于休眠状态, // 线程b也判断不存在,b线程就new了一个。 // 然后a线程wake up,据需执行new volatile获取最新值。 if(volatileThread.isFlag()){ System.out.println("flag 此时为true !"); break; } } } } class VolatileThread implements Runnable{ private volatile boolean flag = false; @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println(Thread.currentThread().getName() + " flag = " + flag); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
运行结果如下图所示:
通过对比我们发现在通过Volatile修饰和不通过Volatile修饰的变量,输出结果竟然会有些偏差。到底是为什么呢?
我们逐步拆解上面代码执行步骤:
1、针对于不使用Volatile关键字修饰变量:
步骤一:默认flag = false;
步骤二main线程的缓存区域没有刷新 flag的值。所以flag 还是false。故没有输出
步骤三:子线程输出 Thread-0 flag = true
2、针对于使用Volatile关键字修饰变量:
步骤一:默认flag = false;
步骤二:主线程看到flag是被Volatile关键字修饰的变量。则获取最新的flag变量值,此时flag = true。故输出
步骤三:子线程输出 Thread-0 flag = true
5. Volatile的优点可见性:被Volatile修饰的变量可以马上刷新主内存中的值,保证其他线程在获取时可以获取最新值,所有线程看到该变量的值均相同。
轻量级的synchronized,高并发下保证变量的可见性。
6.Volatile的缺点1、频繁刷新主内存中变量,可能会造成性能瓶颈
2、不具备操作的原子性,不适合在对该变量的写操作依赖于变量本身自己。例如i++,并不能通过volatile来保证原子性
喜欢就关注我吧文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/75621.html
摘要:假设不发生编译器重排和指令重排,线程修改了的值,但是修改以后,的值可能还没有写回到主存中,那么线程得到就是很自然的事了。同理,线程对于的赋值操作也可能没有及时刷新到主存中。线程的最后操作与线程发现线程已经结束同步。 很久没更新文章了,对隔三差五过来刷更新的读者说声抱歉。 关于 Java 并发也算是写了好几篇文章了,本文将介绍一些比较基础的内容,注意,阅读本文需要一定的并发基础。 本文的...
摘要:因为管理人员是了解手下的人员以及自己负责的事情的。处理器优化和指令重排上面提到在在和主存之间增加缓存,在多线程场景下会存在缓存一致性问题。有没有发现,缓存一致性问题其实就是可见性问题。 网上有很多关于Java内存模型的文章,在《深入理解Java虚拟机》和《Java并发编程的艺术》等书中也都有关于这个知识点的介绍。但是,很多人读完之后还是搞不清楚,甚至有的人说自己更懵了。本文,就来整体的...
摘要:因为管理人员是了解手下的人员以及自己负责的事情的。处理器优化和指令重排上面提到在在和主存之间增加缓存,在多线程场景下会存在缓存一致性问题。有没有发现,缓存一致性问题其实就是可见性问题。 网上有很多关于Java内存模型的文章,在《深入理解Java虚拟机》和《Java并发编程的艺术》等书中也都有关于这个知识点的介绍。但是,很多人读完之后还是搞不清楚,甚至有的人说自己更懵了。本文,就来整体的...
摘要:当某个不应该发布的对象被发布时,这种情况被称为逸出。线程安全共享线程安全的对象在其内部实现同步,因此多线程可以通过对象的公有接口来进行访问而不需要进一步的同步。 前言 本系列博客是对《Java并发编程实战》的一点总结,本篇主要讲解以下几个内容,内容会比较枯燥。可能大家看标题不能能直观的感受出到底什么意思,这就是专业术语,哈哈,解释下,术语(terminology)是在特定学科领域用...
阅读 3196·2021-11-18 10:02
阅读 1446·2021-10-12 10:08
阅读 1236·2021-10-11 10:58
阅读 1269·2021-10-11 10:57
阅读 1167·2021-10-08 10:04
阅读 2121·2021-09-29 09:35
阅读 773·2021-09-22 15:44
阅读 1269·2021-09-03 10:30