java volatile关键字的含义详细介绍
作者:lqh 发布时间:2021-11-01 16:52:17
java volatile关键字
在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉。
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制。
synchronized
同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用
synchronized 修饰的方法 或者 代码块。
volatile
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。
下面看一个例子,我们实现一个计数器,每次线程启动的时候,会调用计数器inc方法,对计数器进行加一
执行环境——jdk版本:jdk1.6.0_31 ,内存 :3G cpu:x86 2.4G
public class Counter
{
public static int count
= 0;
public static void inc()
{
//这里延迟1毫秒,使得结果明显
try {
Thread.sleep(1);
}
catch (InterruptedException
e) {
}
count++;
}
public static void main(String[]
args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i
= 0;
i < 1000;
i++) {
new Thread(new Runnable()
{
@Override
public void run()
{
Counter.inc();
}
}).start();
}
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" +
Counter.count);
}
}
运行结果:Counter.count=
995
实际运算结果每次可能都不一样,本机的结果为:运行结果:Counter.count=
995
,可以看出,在多线程的环境下,Counter.count并没有期望结果是
1000
很多人以为,这个是多线程并发问题,只需要在变量count之前加上
volatile
就可以避免这个问题,那我们在修改代码看看,看看结果是不是符合我们的期望
public class Counter
{
public volatile static int count
= 0;
public static void inc()
{
//这里延迟1毫秒,使得结果明显
try {
Thread.sleep(1);
}
catch (InterruptedException
e) {
}
count++;
}
public static void main(String[]
args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i
= 0;
i < 1000;
i++) {
new Thread(new Runnable()
{
@Override
public void run()
{
Counter.inc();
}
}).start();
}
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" +
Counter.count);
}
}
运行结果:Counter.count=992
运行结果还是没有我们期望的1000,下面我们分析一下原因
在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,
线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存
变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,
在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图
描述这写交互
read and load 从主存复制变量到当前工作内存
use and assign 执行代码,改变共享变量值
store and write 用工作内存数据刷新主存相关内容
其中use and assign 可以多次出现
但是这一些操作并不是原子性,也就是 在read load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样
对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的
例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值
在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6
线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6
导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
猜你喜欢
- 一、背景当前B/S模式已成为应用开发的主流,而在企业办公系统中,常常有客户这样子要求:你要把我们的报表直接用Excel打开(电信系统、银行系
- 前言众所周知在spring boot内,设置session过期时间只需在application.properties内添加server.se
- Java生态圈中有很多处理JSON和XML格式化的类库,Jackson是其中比较著名的一个。虽然JDK自带了XML处理类库,但是相对来说比较
- 一、代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强。加入一些非业务性代码,比如事务、日志、报警发邮件等
- 前述园子里有许多人对log4net这款开源的日志记录控件有很多介绍。在这里个人再做一次总结,希望对以后有所帮助,需要的时候可以直接使用,减少
- 本文实例讲述了C#实现在启动目录创建快捷方式的方法。分享给大家供大家参考。具体如下:添加引用,选择 COM 选项卡并选择 Windows S
- JPA连接到数据库,调用存储过程,这样的需求很常见。本文就针对这一点,讲述如何使用spring Data JPA调用存储过程的方法。1、存储
- 背景:以前学的Java进行开发,多用到Mybatis,Hiberante等ORM框架,最近需要上手一个C#的项目,由于不是特别难,也不想再去
- @Conditional的使用@Conditional可以根据条件来判断是否注入某些Bean。package com.morris.spri
- <html> <head> &nb
- 线程间通信我们看下面的图我们来看线程间通信的原理:线程(Thread B)和线程(Thread A)通信, 首先线程A 必须实现同步上下文对
- 在mybatis中sql是写在xml映射文件中的,如果sql中有一些特殊字符的话,在解析xml文件的时候就会被转义,如若不希望被转义,那该怎
- 二进制数据一般输入的格式是0x45, 0x3a, 0xc3, 这种数据格式看起来是16进制的字符串,但是实际上在存储的时候每个都对应一个字节
- JMF这个多媒体开发框架太牛了,简单的几句代码就能实现一个视频播放器的开发,厉害,就是支持的格式少了一些,没关系,这个视频播放器可以播放mp
- 修改好的代码下载地址:https://github.com/Vico-H/Launcher•不显示开机向导-----------------
- 项目框架采用spring+hibernate+springMVC如果上传文件不想使用flash那么你可以采用HTML5;截图前段模块是boo
- 代码import javax.mail.internet.InternetAddress;import javax.mail.interne
- 这里以JDK 7U15在 Windows x86平台下的安装为例,其他平台安装与此类同。下载JDK请查看:JDK最新下载地址【含下载方法】①
- 缘起经过前面三章的入门,我们大概了解了Mybatis的主线逻辑是什么样子的,在本章中,我们将正式进入Mybatis的源码海洋。Mybatis
- 一、了解面向对象1、概念基本理解:1)、一个个体可以看做是一个对象,例如:人这个个体;2)、有共同属性的一类作为一个个体,例如:学生、白领、