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

Wesley13
• 阅读 709

   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();  
    }  
}
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
volatile 关键字说明
volatile变量修饰的共享变量进行写操作前会在汇编代码前增加lock前缀:1),将当前处理器缓存行的数据写回到系统内存;2),这个写会内存的操作会使其它cpu缓存该内存地址的数据无效。Java语言volatile关键字可以用一句贴切的话来描述“人皆用之,莫见其形“。理解volatile对理解它对理解Java
Wesley13 Wesley13
2年前
Volatile关键字
Volatile关键字①volatile的两个特点1保证线程(CPU)之间的可见性;(也就是保证数据一致性)简单解释一下:一个线程将一个值的数值改变时,另一个使用该数值的线程能看到这种改变;2禁止指令重排序(禁止乱序执行);这个和单例
Wesley13 Wesley13
2年前
Java多线程之volatile详解
目录:什么是volatile?JMM内存模型之可见性volatile三大特性之一:保证可见性volatile三大特性之二:不保证原子性volatile三大特性之三:禁止指令重排小结1.什么是volatile?答:volatile是java虚拟机提供的轻量级的同步机制(
Wesley13 Wesley13
2年前
Java 深入理解volatile关键字
我们知道Java中volatile实现了修饰变量的原子性以及可见性,并且为了实现多线程环境下的线程安全,禁止了指令重排。首先我们先来了解一下happensbefore原则、asifserial语义以及数据依赖性,引用自《Java并发编程的艺术》happensbefore简介从JDK5开始,Java使用新的JSR133内存模型
Wesley13 Wesley13
2年前
JAVA 并发包
Java.Utril.ConcurrentVolatile关键字避免java虚拟机指令重排序,保证共享数据修改同步,数据可见性。volatile相较于synchronized是一种比较轻量级地同步策略,但不具备互斥性,不能成为synchronized的替代,不能保证原子性。
Wesley13 Wesley13
2年前
Java多线程(二)
\恢复内容开始一,volatile关键字当多个线程操作共享数据时,可以保证内存中的数据可见性相较于synchronized关键字:1,不具备“互斥性”2,不能保证变量的原子性二,原子变量volatile保证内存可见性CAS(CompareAndSwap)算法保证数据的原子性内存值V预估值A更新值
Wesley13 Wesley13
2年前
MySQL数据库InnoDB存储引擎Log漫游(1)
作者:宋利兵来源:MySQL代码研究(mysqlcode)0、导读本文介绍了InnoDB引擎如何利用UndoLog和RedoLog来保证事务的原子性、持久性原理,以及InnoDB引擎实现UndoLog和RedoLog的基本思路。00–UndoLogUndoLog是为了实现事务的原子性,
Wesley13 Wesley13
2年前
Java 多线程:volatile关键字
概念volatile也是多线程的解决方案之一。\\volatile能够保证可见性,但是不能保证原子性。\\它只能作用于变量,不能作用于方法。当一个变量被声明为volatile的时候,任何对该变量的读写都会绕过高速缓存,直接读取主内存的变量的值。如何理解直接读写主内存的值:回到多线程生成的原因(Java内存模型与
Wesley13 Wesley13
2年前
Java 开发, volatile 你必须了解一下
并发的三个特性首先说我们如果要使用volatile了,那肯定是在多线程并发的环境下。我们常说的并发场景下有三个重要特性:原子性、可见性、有序性。只有在满足了这三个特性,才能保证并发程序正确执行,否则就会出现各种各样的问题。原子性,上篇文章说到的CAS和Atomic\类,可以保证简单操作的原子性,对