深入理解Java内存模型JMM与volatile关键字

并发编程三大特性:可见性、原子性、有序性
volatile保证可见性与有序性,但是不保证原子性,保证原子性需要借助synchronized这样的锁机制
多核并发缓存架构:
在这里插入图片描述
Java线程内存模型跟cpu缓存模型类似,是基于cpu缓存模型来建立的,Java线程内存模型是标准化的,屏蔽掉了底层不同计算机的区别。
在这里插入图片描述

以下测试程序,测试线程会在工作内存中保存一个共享变量的副本。

package demo6;

public class VolatileTest {
 private static boolean initFlag = false;
 public static void main(String[] args)throws InterruptedException {
	 new Thread(new Runnable(){
		 public void run() {
			 System.out.println("waiting data ....");
		 //死循环
		   while(!initFlag) {
		   }
		   System.out.println("*************sucess!!!");
		 }
		 
	 }).start();
	 
	 Thread.sleep(2000);
	 new Thread(new Runnable() {
		 public void run() {
			 prepareData();
		 }
	 }).start();
 }
 public static void prepareData() {
	 System.out.println("prepareing data .....");	
	 initFlag = true;
	 System.out.println("prepare data end.....");	
} 
}

打印如下:
在这里插入图片描述
由上打印结果可知在prepareData()方法中initFlag的值改变并未影响另一个线程while(initFlag)的值。这是由于多个线程中都有一个副本值,当另一个线程修改了这个值后对其它线程是不可见的。
解决办法:(1.用 volatile 修饰2.synchronized(也会刷新副本中工作内存中的值))

 private static volatile boolean initFlag = false;

所以:volatile 作用之一: 用来修饰共享变量可以达到数据在多线程之间的可见性,就是一个线程修改了这个变量,其它线程知道它修改了。
方法:使用的是MESI缓存一致性协议
JMM数据原子操作

  • read(读取):从主内存读取数据
  • load(载入):将主内存读取到的数据写入工作内存
  • use(使用):从工作内存读取数据来计算
  • assign(赋值):将计算好的值重新赋值到工作内存中
  • store(存储):将工作内存数据写入主内存
  • write(写入):将store过去的变量赋值给主内存中的变量
  • lock(锁写):将主内存变量加锁,标识为线程独占状态
  • unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量
    下面我们来看一下上面加了volatile 关键字后代码在硬件中原子操作的运行流程(JMM内存模型):
    在这里插入图片描述

volatile 缓存可见性实现原理:
底层实现主要是通过汇编lock前缀指令,它会锁定这块内存区域的缓存(缓存行锁定)并回写到主内存
IA-32架构软件开发者手册对lock指令的解释

  1. 会将当前处理缓存行的数据立即写回到系统内存
  2. 这个写回操作会引起在其它cpu里缓存了该内存地址的数据无效(即MESI协议)

我们来看一下volatile不支持的原子性

package demo6;

public class VolatileTest1 {
    public static volatile int num =0;
    public static void increase() {
    	num++;
    }
	public static void main(String[] args) throws InterruptedException {
	   Thread[] threads = new Thread[10];
	   for(int i=0;i<threads.length;i++) {
		     threads[i] = new Thread(new Runnable(){
				@Override
				public void run() {
					for(int i=0;i<1000;i++) { 
						increase();
					}
				}
		     });
		     threads[i].start();
	   }
         for(Thread t:threads) {
        	 t.join();
         }
         System.out.println(num);
	}
}

如果volatile保证原子性则打印结果应为10000
但结果打印如下:

在这里插入图片描述
每一次打印的结果都不一样,这是因为其它线程同时也做自增操作时从新去主存read时,原来自增的值会丢失(出现了写覆盖)。
解决办法:1.加synchronized(2.AtomicInteger 封装类)

package demo6;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileTest1 {
    public static volatile int num =0;
    public static void increase() {
    	num++;
    }
    static AtomicInteger atomicInteger = new AtomicInteger();
    public static void addMyAtomic() {
    	atomicInteger.getAndIncrement(); //每次自增1
    	
    }
	public static void main(String[] args) throws InterruptedException {
	   Thread[] threads = new Thread[10];
	   for(int i=0;i<threads.length;i++) {
		     threads[i] = new Thread(new Runnable(){
				@Override
				public void run() {
					for(int i=0;i<1000;i++) { 
						increase();
						addMyAtomic();
					}
				}
		     });
		     threads[i].start();
	   }
         for(Thread t:threads) {
        	 t.join();
         }
         System.out.println("不保证原子性:"+num);
         System.out.println("保证原子性:"+atomicInteger);
	}
}

打印结果为:
在这里插入图片描述

本博文内容出自原作者链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
后台采用apache服务器下的cgi处理c语言做微信小程序后台逻辑的脚本映射。PC端的服务器和客户端都是基于c语言写的。采用mysql数据库进行用户数据和聊天记录的存储。.zip C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值