Juc12_Volatile的可见性、不保证可见性、有序性、使用、内存屏障四大指令StoreStore、StoreLoad 、LoadLoad、LoadStore(三)

简介: ③. volatile特性①. 保证可见性②. 不保证原子性

③. volatile特性


①. 保证可见性


  • ①. 保证不同线程对这个变量进行操作时的可见性,即变量一旦改变所有线程立即可以看到


  • ②. 代码展示
    不加volatile,没有可见性,程序无法停止
    加了volatile,保证可见性,程序可以停止


/*
验证volatile的可见性:
1.加入int number=0; number变量之前没有添加volatile关键字修饰,没有可见性
2.添加了volatile,可以解决可见性问题
 * */
class Resource{
   //volatile int number=0;
   volatile int number=0;
   public void addNumber(){
       this.number=60;
   }
}
public class Volatile_demo1 {
   public static void main(String[] args) {
       Resource resource=new Resource();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t coming ");
            try {TimeUnit.SECONDS.sleep(4);}catch (InterruptedException e){e.printStackTrace();}
            resource.addNumber();
            System.out.println(Thread.currentThread().getName()+"\t update "+resource.number);
        },"线程A").start();
        //如果主线程访问resource.number==0,那么就一直进行循环
        while(resource.number==0){
        }
        //如果执行到了这里,证明main现在通过resource.number的值为60
       System.out.println(Thread.currentThread().getName()+"\t"+resource.number);
   }
}


③. 上述代码原理解释


没有添加volatile关键字,线程A对共享变量改变了以后(number=60),主线程(这里的线程B)访问number的值还是0,这就是不可见


添加volatile之后,线程A对共享数据进行了改变以后,那么main线程再次访问,number的值就是改变之后的number=60


微信图片_20220108124722.png


②. 不保证原子性


  • ①. 代码展示
    (我们对20个线程进行循环100次的操作)


public class Volatile_demo3 {
    public static void main(String[] args) {
       /* System.out.println(Thread.activeCount());*/
        AutoResource autoResource=new AutoResource();
        //20个线程每个线程循环100次
        for (int i = 1; i <=20; i++) {
            new Thread(()->{
                for (int j = 1; j <=100; j++) {
                    autoResource.numberPlusPlus();
                    autoResource.addAtomicInteger();
                }
            },String.valueOf(i)).start();
        }
        //需要等待上面20个线程都全部计算完后,再用main线程取得的最终的结果值是多少
        //默认有两个线程,一个main线程,二是后台gc线程
         while(Thread.activeCount()>2){
             Thread.yield();
         }
        System.out.println(Thread.currentThread().getName()+"\t int type"+autoResource.number);
        System.out.println(Thread.currentThread().getName()+"\t AutoInteger type"+autoResource.atomicInteger.get());
    }
}
class AutoResource{
    volatile int number=0;
    public void numberPlusPlus(){
        number++;
    }
    //使用AutoInteger保证原子性
    AtomicInteger atomicInteger=new AtomicInteger();
    public void addAtomicInteger(){
        atomicInteger.getAndIncrement();
    }
}


②. 对于一读一写操作,不会有数据问题

(假设主内存的共享变量number=1,需要对主内存的number++处理,对于两个线程t1、t2如果是一读一写的操作(不会有数据丢失的情况),某一时刻,t1抢到CPU的执行权,将共享数据1读回t1的工作内存,进行number++的操作,这个时候number=2,将2从工作内存写回到主内存中。写回后马上通知t2线程,将number=2读到t2的工作线程)


③. 对于两个写,会出现数据问题


(假设主内存的共享变量number=0,需要对主内存进行10次的number++处理,最终的结果就是10,对于两个线程t1、t2如果是两个写的操作(会造成数据丢失的情况),t1和t2将主内存的共享数据读取到各自的工作内存去,某一时刻,t1线程抢到CPU的执行权,进行

number++的处理,将工作内存中的number=1写回到主内存中,就在这一刻,t2也抢到CPU执行权,进行number++的处理,这个时候number++后的结果也等于1,t1将number=1写回到主内存中去,并通知t2线程,将主内存中的number=1读到t2的工作内存中去,这个时候对于t2,它之前也进行了一次number++的操作将会无效,回重新进行一次number++的操作。这也数据也就写丢了一次,那么10次number++后的结果也就不会等于10)


read-load-use 和 assign-store-write 成为了两个不可分割的原子操作,但是在use和assign之间依然有极小的一段真空期,有可能变量会被其他线程读取,导致写丢失一次.


微信图片_20220108124758.png




相关文章
|
6月前
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
102 1
|
7月前
|
存储 程序员 数据处理
【汇编】mov和add指令、确定物理地址的方法、内存分段表示法
【汇编】mov和add指令、确定物理地址的方法、内存分段表示法
754 1
【汇编】mov和add指令、确定物理地址的方法、内存分段表示法
|
7月前
|
存储
【汇编】数据在哪里?有多长、div指令实现除法、dup设置内存空间
【汇编】数据在哪里?有多长、div指令实现除法、dup设置内存空间
142 1
|
缓存 安全 Java
volatile底层的实现原理:volatile关键字的作用、内存模型、JMM规范和CPU指令
volatile底层的实现原理:volatile关键字的作用、内存模型、JMM规范和CPU指令
166 0
持久内存指令(PMDK)简介
持久内存指令(PMDK)简介
330 0
|
存储 安全
4.3 x64dbg 搜索内存可利用指令
发现漏洞的第一步则是需要寻找到可利用的反汇编指令片段,在某些时候远程缓冲区溢出需要通过类似于`jmp esp`等特定的反汇编指令实现跳转功能,并以此来执行布置好的`ShellCode`恶意代码片段,`LyScript`插件则可以很好的完成对当前进程内存中特定函数的检索工作。在远程缓冲区溢出攻击中,攻击者也可以利用汇编指令`jmp esp`来实现对攻击代码的执行。该指令允许攻击者跳转到堆栈中的任意位置,并从那里执行恶意代码。
200 0
|
存储 缓存 索引
通过地址和索引实现数组、CPU指令执行过程、内存概述及内存物理结构
通过地址和索引实现数组、CPU指令执行过程、内存概述及内存物理结构
112 0
|
23天前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
192 1
|
13天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
21天前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80