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

   volatile关键字:

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

         volatile如何实现内存可见性:

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

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

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

         线程写volatile变量的过程:

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

         线程读volatile变量的过程:

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

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

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

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

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

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

          使用synchronized关键字

1import java.util.concurrent.ExecutorService; 2import java.util.concurrent.Executors; 3 4/** 5 * @author InJavaWeTrust 6 */ 7public class TestSyn implements Runnable { 8 9 private int number = 0; 10 11 public int getNumber() { 12 return this.number; 13 } 14 15 public void run() { 16 increase(); 17 } 18 19 public void increase() { 20 synchronized (this) { 21 this.number++; 22 } 23 } 24 25 public static void main(String[] args) { 26 ExecutorService exec = Executors.newFixedThreadPool(1000); 27 TestSyn syn = new TestSyn(); 28 for (int i = 0; i < 1000; i++) { 29 exec.submit(syn); 30 } 31 System.out.println("number : " + syn.getNumber()); 32 exec.shutdown(); 33 } 34}

 使用ReentrantLock

1import java.util.concurrent.ExecutorService; 2import java.util.concurrent.Executors; 3import java.util.concurrent.locks.Lock; 4import java.util.concurrent.locks.ReentrantLock; 5/** 6 * @author InJavaWeTrust 7 */ 8public class TestRee implements Runnable { 9 10 private Lock lock = new ReentrantLock(); 11 private int number = 0; 12 13 public int getNumber() { 14 return this.number; 15 } 16 17 public void run() { 18 increase(); 19 } 20 21 public void increase() { 22 lock.lock(); 23 try { 24 this.number++; 25 } finally { 26 lock.unlock(); 27 } 28 } 29 30 public static void main(String[] args) { 31 TestRee ree = new TestRee(); 32 ExecutorService exec = Executors.newFixedThreadPool(1000); 33 for (int i = 0; i < 1000; i++) { 34 exec.submit(ree); 35 } 36 System.out.println("number : " + ree.getNumber()); 37 exec.shutdown(); 38 } 39}

 使用AtomicInteger

1import java.util.concurrent.ExecutorService; 2import java.util.concurrent.Executors; 3import java.util.concurrent.atomic.AtomicInteger; 4 5/** 6 * @author InJavaWeTrust 7 */ 8public class TestAtomic implements Runnable { 9 10 private static AtomicInteger number = new AtomicInteger(0); 11 12 public void run() { 13 increase(); 14 } 15 16 public void increase() { 17 number.getAndAdd(1); 18 } 19 20 public static void main(String[] args) { 21 TestAtomic ato = new TestAtomic(); 22 ExecutorService exec = Executors.newFixedThreadPool(1000); 23 for (int i = 0; i < 1000; i++) { 24 exec.submit(ato); 25 } 26 System.out.println("number : " + number.get()); 27 exec.shutdown(); 28 } 29}
点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Volatile关键字

Volatile关键字①volatile的两个特点1保证线程(CPU)之间的可见性;(也就是保证数据一致性)简单解释一下:一个线程将一个值的数值改变时,另一个使用该数值的线程能看到这种改变;2禁止指令重排序(禁止乱序执行);这个和单例

volatile 关键字说明

volatile变量修饰的共享变量进行写操作前会在汇编代码前增加lock前缀:1),将当前处理器缓存行的数据写回到系统内存;2),这个写会内存的操作会使其它cpu缓存该内存地址的数据无效。Java语言volatile关键字可以用一句贴切的话来描述“人皆用之,莫见其形“。理解volatile对理解它对理解Java

Java多线程之volatile详解

目录:什么是volatile?JMM内存模型之可见性volatile三大特性之一:保证可见性volatile三大特性之二:不保证原子性volatile三大特性之三:禁止指令重排小结1.什么是volatile?答:volatile是java虚拟机提供的轻量级的同步机制(

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