volatile保证可见性

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

  1)保证了不同线程对这个变量进行操作的可见性,即一个线程修改了某个变量的值,这新值对其它线程来说是立即可见的

  2)禁止进行指令重排序

 volatile不能确保原子性

 在访问volatile变量时,不会执行加锁操作,因此也就不会执行线程阻塞。所以volatile变量是一种比sychhronized关键字更轻量级的同步机制

 关于这一点,可看个例子:

public class VolatileDemo {

    private volatile int number = ;

    public int getNumber() {
return this.number;
} public void add() {
this.number++;
} public static void main(String[] args) {
final VolatileDemo demo = new VolatileDemo();
for (int i = ; i < ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
demo.add();
}
}).start();
} // 如果还有子线程在运行,主线程就让出CPU资源
// 直到所有的子线程都运行完了,主线程再继续往下执行
while (Thread.activeCount() > ) {
Thread.yield();
}
System.out.println("number:" + demo.getNumber());
} }

大家想一下这段程序的输出结果是多少?也许有些朋友认为是500,但是事实上运行它会发现偶尔会出现小于500的值。如果在number++前,加一个Thread.sleep(),效果更明显

接下来,我们分析一下为啥会出现这种情况:

假如某个时刻变量number=5

1. 线程A读取number的值,然后阻塞

2. 线程B读取number的值

3. 线程B执行加1操作

4. 线程B写入最新的number的值(6)到主内存中。此时线程A工作内存中number还是5

5. 线程A执行加1操作

6. 线程A写入最新的number值(6)到主内存中

两个线程分别进行了一次自增操作后,number只增加了1。

根源就在自增操作不是原子性操作,而volatile也无法保证对变量的任何操作都是原子性

解决方案:可以通过synchronized或lock,进行加锁,来保证操作的原子性。也可以通过AtomicInteger。

具体例子:

    public void add2() {
// 加上synchronized后,就可以保证原子性
synchronized (this) {
this.number++;
}
} public void add3() {
// 加上lock后,保证原子性
lock.lock();
try {
this.number++;
} finally {
lock.unlock();
}
} //AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减
private AtomicInteger count = new AtomicInteger(); public void add4() {
count.getAndIncrement();
}

volatile的适用场景:

  1、对变量的写入操作不依赖其当前值(如 number++ 、count = count * 5等)

  2、该变量没有包含在具有其它变量的不变式中

3、volatile的一个重要作用就是和CAS结合,保证了原子性,详细的可以参见java.util.concurrent.atomic包下的类,比如AtomicInteger

参考资料:http://www.importnew.com/24082.html

  

volatile特性的更多相关文章

  1. volatile特性及内存语义

    1.volatile变量自身具有下列特性:·可见性:对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入.·原子性:对任意单个volatile变量的读/写具有原子 ...

  2. java - jmm之volatile特性

    volatile是什么? volatile是JVM提供的一种轻量级的同步机制,其具有三个特性. 保证可见性 不保证原子性 禁止指令重排 保证可见性 JMM(java memory model)中文翻译 ...

  3. volatile关键字的特性及证明

    volatile是java虚拟机提供的轻量级的同步机制 JMM(Java内存模型)是围绕着并发编程中原子性.可见性.有序性这三个特征来建立的 原子性:一个操作或多个操作要么全部执行完成且执行过程不被中 ...

  4. volatile的特性

    volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这 ...

  5. java内存模型-volatile

    volatile 的特性 当我们声明共享变量为 volatile 后,对这个变量的读/写将会很特别.理解 volatile 特性的一个好方法是:把对 volatile 变量的单个读/写,看成是使用同一 ...

  6. java 中的volatile

    本博客摘录自   http://www.infoq.com/cn/articles/java-memory-model-4/ 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解 ...

  7. synchronized同步块和volatile同步变量

    Java语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量.这两种机制的提出都是为了实现代码线程的安全性.其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而 ...

  8. 深入理解Java内存模型(四)——volatile

    volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这 ...

  9. 【转】深入理解Java内存模型(四)——volatile

    volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这 ...

随机推荐

  1. artdialog(4.1.7)弹出框

    artDialog弹出框 <link href="/js/artDialog/skins/default.css" rel="stylesheet" /& ...

  2. c# is 和 as 的区别和使用

    1:is 是判断类型,用于检查对象是否与给定类型兼容,不成功则不会抛出异常,如果兼容则返回true,如果不兼容则返回false.在进行类型转换之前用 f (P_obj is System.String ...

  3. 使用filter进行登录验证,并解决多次重定向问题

    最近在做关于filter登录验证的功能,防止未登录的用户直接通过地址进入系统 LoginFilter类:继承Filter接口 package com.ss.filter; import java.io ...

  4. C++实现-特征码遍历

    #include <stdio.h> #include <stdlib.h> #include <windows.h> union Base { DWORD add ...

  5. 盗墓笔记—阿里旺旺ActiveX控件imageMan.dll栈溢出漏洞研究

    本文作者:i春秋作家——cq5f7a075d 也许现在还研究Activex就是挖坟,但是呢,笔者是摸金校尉,挖坟,呸!盗墓是笔者的本职工作. 额,不扯了,本次研究的是阿里旺旺ActiveX控件imag ...

  6. (samba启动失败)smb.service: main process exited, code=exited, status=1/FAILURE

    按照指示,前往:journalctl -xe 没什么发现,搜的时候有人说也可以查看 journalctl -r 打出来之后我也看不出什么门道来 又看到有人说 smb 方面可以看看 testparm 我 ...

  7. webpack快速入门——CSS文件打包

    1.在src下新建css文件,在css文件下新建index.css文件,输入以下代码 body{ background:pink; color:yellowgreen; } 2.css建立好后,需要引 ...

  8. java验证身份证合理性

    package com.tiantian.util; import java.util.Calendar;import java.util.HashMap;import java.util.Map;i ...

  9. Tomcat几种出错方法

    1.  Several ports (8005, 8089, 8009) required by Tomcat v8.0 Server at localhost are already in use. ...

  10. 初识PHP之php运行流程及原理(一)

    初识PHP一.用脚本命令行运行php(1)打开cmd.exe(winkey+R)(2)找到php.exe(拖进cmd即可)(3)输入命令php.exe -f "文件实际路径"注:运 ...