volatile实现可见性但不保证原子性

volatile关键字:

  • 能够保证volatile变量的可见性
  • 不能保证volatile变量复合操作的原子性

volatile如何实现内存可见性:

深入来说:通过加入内存屏障和禁止重排序优化来实现的。

  • 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
  • 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令

通俗地讲:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值。

线程写volatile变量的过程:

  • 改变线程工作内存中volatile变量副本的值
  • 将改变后的副本的值从工作内存刷新到主内存

线程读volatile变量的过程:

  • 从主内存中读取volatile变量的最新值到线程的工作内存中
  • 从工作内存中读取volatile变量的副本

volatile不能保证volatile变量复合操作的原子性:

private int number = 0;
number++; //不是原子操作

它分为三步:
         读取number的值
         将number的值加1
         写入最新的number的值

保证number自增操作的原子性:

  • 使用synchronized关键字
  • 使用ReentrantLock
  • 使用AtomicInteger

使用synchronized关键字

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @author InJavaWeTrust
*/
public class TestSyn implements Runnable { private int number = 0; public int getNumber() {
return this.number;
} public void run() {
increase();
} public void increase() {
synchronized (this) {
this.number++;
}
} public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(1000);
TestSyn syn = new TestSyn();
for (int i = 0; i < 1000; i++) {
exec.submit(syn);
}
System.out.println("number : " + syn.getNumber());
exec.shutdown();
}
}

使用ReentrantLock

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author InJavaWeTrust
*/
public class TestRee implements Runnable { private Lock lock = new ReentrantLock();
private int number = 0; public int getNumber() {
return this.number;
} public void run() {
increase();
} public void increase() {
lock.lock();
try {
this.number++;
} finally {
lock.unlock();
}
} public static void main(String[] args) {
TestRee ree = new TestRee();
ExecutorService exec = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
exec.submit(ree);
}
System.out.println("number : " + ree.getNumber());
exec.shutdown();
}
}

使用AtomicInteger

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger; /**
* @author InJavaWeTrust
*/
public class TestAtomic implements Runnable { private static AtomicInteger number = new AtomicInteger(0); public void run() {
increase();
} public void increase() {
number.getAndAdd(1);
} public static void main(String[] args) {
TestAtomic ato = new TestAtomic();
ExecutorService exec = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
exec.submit(ato);
}
System.out.println("number : " + number.get());
exec.shutdown();
}
}

volatile实现可见性但不保证原子性的更多相关文章

  1. Volatile 只保证可见性,并不保证原子性

    [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article/details/52525724   在说明Java多线程内存可见性之前,先来简单了解一下J ...

  2. volatile实现原理--为什么实现了可见性却不能保证原子性

    本篇文章我们来解决一个问题  这也是面试面的比较多的问题,进阶阶段(高级)一般都会问到. volatile变量怎么保证可见性  为什么在并发情况下无法保证原子性? 比较懒了  摘了一段JVM原理的片段 ...

  3. Java并发_volatile实现可见性但不保证原子性

    读后感 介绍了volatile实现可见性的基本原理 介绍了volatile不能实现原子性的示例,volatile复合操作不能实现原子性,读取值后在自增前改值可能被其它线程读取并修改,自增后刷新值可能会 ...

  4. Java线程-volatile不能保证原子性

    下面是一共通过volatile实现原子性的例子: 通过建立100个线程,计算number这个变量最后的结果. package com.Sychronized; public class Volatil ...

  5. Java并发编程之验证volatile不能保证原子性

    Java并发编程之验证volatile不能保证原子性 通过系列文章的学习,凯哥已经介绍了volatile的三大特性.1:保证可见性 2:不保证原子性 3:保证顺序.那么怎么来验证可见性呢?本文凯哥(凯 ...

  6. Volatile不保证原子性(二)

    Volatile不保证原子性 前言 通过前面对JMM的介绍,我们知道,各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存进行操作后在写回到主内存中的. 这就可能存在一个线程AAA修改 ...

  7. 为什么volatile能保证有序性不能保证原子性

    对于内存模型的三大特性:有序性.原子性.可见性. 大家都知道volatile能保证可见性和有序性但是不能保证原子性,但是为什么呢? 一.原子性.有序性.可见性 1.原子性: (1)原子的意思代表着-- ...

  8. CAS (多线程保证原子性)

    面试必问的CAS,你懂了吗? 置顶 2018年03月14日 22:29:19 程序员囧辉 阅读数:29612   版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csd ...

  9. 聊聊高并发(十九)理解并发编程的几种&quot;性&quot; -- 可见性,有序性,原子性

    这篇的主题本应该放在最初的几篇.讨论的是并发编程最基础的几个核心概念.可是这几个概念又牵扯到非常多的实际技术.比方Java内存模型.各种锁的实现,volatile的实现.原子变量等等,每个都可以展开写 ...

随机推荐

  1. swift之属性

    知识点总结: 1.存储属性 struct Town{ let region = "South" //只读属性 var population = //读写属性 } 2.惰性存储属性 ...

  2. oracle初试、函数、增删改查、多表查询

      安装oracle后的测试以及解锁账户                  安装后打开命令行,输入 sqlplus 回车后会提示输入用户名,输入 sys或者system 回车后输入密码,密码为安装or ...

  3. 2. struct A 和 typedef struct A

    2. struct A 和 typedef struct A 2.1 struct A struct A{}定义一个名为struct A的结构体. 下例定义了struct A同时,声明了两个变量(注意 ...

  4. 小程序上拉下拉共存时不可使用scroll-view的解决方法

    使用 bindscrolltolower ,必须搭配使用的 scroll-view 会导致小程序 "enablePullDownRefresh": true 下拉不能使用. 解决方 ...

  5. print语句中逗号(,)和反斜杠(\)的区别

    逗号结尾:   禁止输出换行反斜杠结尾:强制输出换行 >>> print ('A','B') #用一个逗号结尾就可以禁止输出换行 A B >>> print ('A ...

  6. MongoDB 原子操作

    mongodb不支持事务,所以,在你的项目中应用时,要注意这点.无论什么设计,都不要要求mongodb保证数据的完整性. 但是mongodb提供了许多原子操作,比如文档的保存,修改,删除等,都是原子操 ...

  7. C++编译连接过程中关于符号表的报错分析

    是这样的,在学习郑莉老师的多文件结构和编译预处理命令章节时候,看到书里有这么一张图描述如下:#include指令作用是将指定的文件嵌入到当前源文件中#include指令所在的位置. 然后我就想5_10 ...

  8. Gradle 1.12用户指南翻译——第四十七章. Build Init 插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  9. Bootstrap3 栅格系统-实例:响应列重置(Responsive column resets)

    四层的网格你肯定会遇到问题,可用在特定的断点,你的列不清楚作为一个比另一个高完全正确.为了解决这个问题,结合使用.clearfix和响应的实用工具类. <div class="row& ...

  10. TCP发送源码学习(2)--tcp_write_xmit

    一.tcp_write_xmit()将发送队列上的SBK发送出去,返回值为0表示发送成功.函数执行过程如下:1.检测拥塞窗口的大小.2.检测当前报文是否完全处在发送窗口内.3.检测报文是否使用nagl ...