ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

JUC练习15——JMM(java内存模型)

2022-02-02 23:32:43  阅读:189  来源: 互联网

标签:JUC java int static 线程 内存 JMM volatile 操作


链接:https://blog.csdn.net/weixin_44841312/article/details/120911816

一,什么是JMM?

它是一种java内存中数据存储的的协议规则:规定了内存是所有线程共有的,每个线程都有自己的工作内存,当进程需要使用数据时会去内存中读取数据放入自己的工作内存,然后对工作内存的数据进行操作,最终将操作后的数据写回到内存中。

二,八大操作

其实就是读写数据的原子操作,保证数据的读写不出现问题。

1,read操作:将数据从堆栈中读出来到一个缓冲区A

2,load操作:将缓冲区A的数据写入到工作内存

3,use操作:线程读取工作内存的数据进行计算

4,assign操作:线程在CPU中执行,将计算好的值存储到工作内存中

5,store操作:将工作内存大数据写入缓冲区B

6,write操作:将缓冲区B的数据写入原有的为止,对其进行赋值操作

7,lock操作:将堆栈中的变量加锁,标识为内存独占状态

8,unlock操作:堆栈中的变量解锁,其它线程就可以锁定该变量

 

三,volatile

 保证可见性(线程A修改的数据对线程B来说是可见的)
不保证原子性(例如a++实现的a+1操作其实是有多个步骤组成,他们是可分割的),synchronize保证代码操作原子性
保证有序性(代码的执行过程中,为了提高执行的效率会发生执行的重新排列)
1,可见性验证:
 private static int flag = 0;
    public static void main(String[] args)  {
        //主线程写一个死循环
        new Thread(()->
        {
            while (flag==0)
            {
                System.out.println("a");
            }
            flag = 1;
        }).start();
        //终止死循环
        flag=1;
        System.out.println(flag);
        //结果:无法终止死循环
        //解决,使用volatile:private static volatile int flag = 0;
        //说明volatile可以保证程序的可见性
    }

  2,验证volatile不保证原子性

private volatile static int sum = 0;
    private volatile static AtomicInteger sum1 = new AtomicInteger(0);
    public static void main(String[] args)  {
        //理论上的值为50000,但是由于volatile不能保证原子性,所以达不到50000
        for (int i=0;i<50;i++)
        {
            new Thread(()->
            {
                for (int j=0;j<1000;j++)
                {
                    //其实它在底层的步骤为:获取值,加1,写回值
                    sum++;
                    sum1.getAndIncrement();
                }
            }).start();
        }
        //保存计算的线程都执行完毕,留下的线程为main和gc
        while (Thread.activeCount()>2)
        {
            Thread.yield();
        }
        System.out.println(sum);
        System.out.println(sum1);
    }

3,保证有序性,防止指令重排

什么是指令重排:我们写的程序真正执行是并不一定会按照我们认为的顺序执行的,为了提高程序的执行效率,

编译器优化时会发生重排,执行指令并行时会发生重排,内存系统也会发生重排

编译器在执行指令重拍时,会考虑数据间的依赖性

int y =1 //1

int y=2  //2

x=x+5  //3

y=x*x  //4

我们期望执行的顺序:1234,最终执行的顺序可能是2134  1324,但不可能是4123,这由数据间的依赖性保证的

但是有时的指令重排是会导致数据出错的,此时如果加上了volatile关键字,就会在执行相应的命令组前后加上一组内存屏障,

在内存屏障中的指令就不会发生指令重排

 

 内存屏障在单例模式中用的最多,单例模式使用枚举的方式创建是最适合的

public enum Singleton
 {
    INSTANCE;//该枚举类对象的实例
    public void doSomething()
    {
        System.out.println("该枚举类实现的方法");
    }
 }

//调用的方式
public class Main {
    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }
}

  


标签:JUC,java,int,static,线程,内存,JMM,volatile,操作
来源: https://www.cnblogs.com/chenjianjiang/p/15860631.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有