volatile理解

  Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和volatile 关键字机制。volatile具有synchronized关键字的“可见性”,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。

特性

1、保证内存可见性

  各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存操作后再写回主内存中的。这就可能存在一个线程AAA修改了共享变量X的值还未写回主内存中时 ,另外一个线程BBB又对内存中的一个共享变量X进行操作,但此时A线程工作内存中的共享比那里X对线程B来说并不不可见.这种工作内存与主内存同步延迟现象就造成了可见性问题。Java提供了volatile来保证可见性,当一个变量被volatile修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,其他线程读取共享变量时,会直接从主内存中读取。

2、不保证原子性

  原子性在一个操作是不可中断的,要么全部执行成功要么全部执行失败。如a++,a+=1就不是原子性操作,volatile不能保证原子性。

3、禁止指令重排序

计算机在执行程序时,为了提高性能,编译器和处理器常常会做指令重排:
  1. 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致.
  2. 处理器在进行重新排序是必须要考虑指令之间的数据依赖性
  3. 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程使用的变量能否保持一致性是无法确定的,结果无法预测

代码

保证内存可见性

package com.raicho.mianshi.myvolatile;

public class MyVolatileVisibility {

 //   private int i;
private volatile int i; public void changeI(int i) {
this.i = i;
} public static void main(String[] args) {
// System.out.println("没有加volatile关键字");
MyVolatileVisibility myVolatile = new MyVolatileVisibility(); new Thread(() -> { try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} myVolatile.changeI(1);
System.out.println(Thread.currentThread().getName()+"修改了i="+myVolatile.i);
},"线程1 ").start(); System.out.println( Thread.currentThread().getName()+"访问 i = "+ myVolatile.i);
while (myVolatile.i == 0) { } } }

当没有加volatile关键字时,当主线程先访问了i值为0,后线程1再进行修改i=1,回到mian线程,并不能察觉到i的值修改为1,然而一直在while循环不能结束,加上volatile关键字就能够检测到其他线程已经将i的值修改为1,结束程序。

不保证原子性

package com.raicho.mianshi.myvolatile;

public class VolatileAtomicity {
volatile int number = 0; public void addNum(){
number++;
} public static void main(String[] args) {
VolatileAtomicity va = new VolatileAtomicity(); for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
va.addNum();
}
},String.valueOf(i)).start();
} //等等20条线程完成
while (Thread.activeCount() >2){
Thread.yield();
} System.out.println(Thread.currentThread().getName()+" number = "+va.number);
}
}

通过代码验证并最终number并不能达到20000,证明volatile并不保证原子性操作

解决方案

  1. 在addNum()方法上加锁synchronized关键字,肯定是可以解决的,但是synchronized加锁太重了,严重降低效率
  2. 使用AtomicInteger类
package com.raicho.mianshi.myvolatile;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileAtomicity {
volatile int number = 0; public void addNum(){
number++;
} AtomicInteger atomicInteger = new AtomicInteger(); public void addNumAtomicInteger(){
atomicInteger.getAndIncrement();
} public static void main(String[] args) {
VolatileAtomicity va = new VolatileAtomicity(); for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
//va.addNum();
va.addNumAtomicInteger();
}
},String.valueOf(i)).start();
} //等等20条线程完成
while (Thread.activeCount() >2){
Thread.yield();
} // System.out.println(Thread.currentThread().getName()+" number = "+va.number);
System.out.println(Thread.currentThread().getName()+" number = "+va.atomicInteger);
}
}

禁止指令重排序

public void mySort(){
int x=11;//语句1
int y=12;//语句2
x=x+5;//语句3
y=x*x;//语句4
}

重新排序后可能会变为
1234
2134
1324
问题:
请问语句4 可以重排后变成第一条码?
存在数据的依赖性 没办法排到第一个

单例模式中使用双重检测机制

public class SingletonDemo {

    private static volatile SingletonDemo instance=null;
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 构造方法");
} /**
* 双重检测机制
* @return
*/
public static SingletonDemo getInstance(){
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
} public static void main(String[] args) {
for (int i = 1; i <=10; i++) {
new Thread(() ->{
SingletonDemo.getInstance();
},String.valueOf(i)).start();
}
}
}

在多线程下,不加volatile关键字也可能出现指令重排的情况,是线程不安全的

Java并发编程volatile关键字的更多相关文章

  1. Java并发编程 Volatile关键字解析

    volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...

  2. java并发编程 volatile关键字 精准理解

    1.volatile的作用 一个线程共享变量(类的成员变量.类的静态成员变量等)被volatile修饰之后,就具有以下作用: 1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修 ...

  3. Java 并发:volatile 关键字解析

    摘要: 在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保 ...

  4. java并发系列(六)-----Java并发:volatile关键字解析

    在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...

  5. 一起来看看java并发中volatile关键字的神奇之处

    并发编程中的三个概念: 1.原子性 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行. 2.可见性 对于可见性,Java提供了volati ...

  6. java并发:volatile关键字

    java并发需要保证原子性,可见性,有序性. http://www.cnblogs.com/expiator/p/9226775.html 一.volatile关键字作用如下: 1.volatile关 ...

  7. Java 并发编程——volatile与synchronized

    一.Java并发基础 多线程的优点 资源利用率更好 程序设计在某些情况下更简单 程序响应更快 这一点可能对于做客户端开发的更加清楚,一般的UI操作都需要开启一个子线程去完成某个任务,否者会容易导致客户 ...

  8. Java多线程编程——volatile关键字

    (本篇主要内容摘自<Java多线程编程核心技术>) volatile关键字的主要作用是保证线程之间变量的可见性. package com.func; public class RunThr ...

  9. Java并发编程--Volatile详解

    摘要      Volatile是Java提供的一种弱同步机制,当一个变量被声明成volatile类型后编译器不会将该变量的操作与其他内存操作进行重排序.在某些场景下使用volatile代替锁可以减少 ...

随机推荐

  1. 【Linux网络基础】 DNS:介绍、作用、解析原理

    1. DNS是什么?   DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去+·记住能够被机器直接读 ...

  2. layui模块化加载Echarts图表v4.2.1

    layui.use(['jquery','echarts'], function () { var $ = layui.$; //记得这是dom对象不是JQ对象,需要转换 echarts = layu ...

  3. Angular 7开发环境配置

    目录 前言 一.搭建项目  1.安装Angular CLI  2.创建项目  3.集成Element Angular 二.设置路由  1.创建路由模块  2.导入.导出RouterModule类  3 ...

  4. jmeter的教学视频

    转载于:https://www.cnblogs.com/ios9/p/9769058.html

  5. TCP链接的三次握手与四次断开

    一直总觉得三次握手和四次断开,之前老师讲的有问题,经过自己再次琢磨,发现是的,老师讲的没毛病,这次也把自己的理解总结一下,让对这个知识模糊的小伙伴再换种思路去理解 首先看一下TCP三次握手发生了哪些: ...

  6. Hyperf基础教程

    前提说明 本教程适用于新手.老手,也适用于任何操作系统,包括Windows.linux.MacOS 介绍 Hyperf 是基于 Swoole 4.4+ 实现的高性能.高灵活性的 PHP 协程框架,内置 ...

  7. 数学--数论--Alice and Bob (CodeForces - 346A )推导

    It is so boring in the summer holiday, isn't it? So Alice and Bob have invented a new game to play. ...

  8. Codeforces Round #618 (Div. 2)-Non-zero

    Guy-Manuel and Thomas have an array a of n integers [a1,a2,-,an]. In one step they can add 1 to any ...

  9. 2020年ubuntu1804安装nginx最新稳定版1.16详细教程笔记

    第一次使用nginx是2007年,当时主流还是apache.nginx横空出世,在web2.0的推动下,迅速崛起.眼下已是绝对的主流了. 当时,还有一个轻量级的lighttpd,是德国人写,刚开始还并 ...

  10. golang之method

    method Go does not have classes. However, you can define methods on types. package main import ( &qu ...