精华内容
下载资源
问答
  • volatile原理

    千次阅读 2018-10-15 18:08:37
    java 使用volatile修饰符是线程之间可以共享变量,一个变量用volatile修饰,...介绍volatile原理之前,先来简单了解下java的内存模型 我们在执行我们的程序的时候,jvm根据自己的内存模型会把内存分为:方法区 Meth...

    java 使用volatile修饰符是线程之间可以共享变量,一个变量用volatile修饰,意味着在多个线程情况下此变量是可见并且是一致的;在特定情况下volatile比锁拥有更好的性能

    volatile int count;
    

    volatile 原理

    介绍volatile原理之前,先来简单了解下java的内存模型

    我们在执行我们的程序的时候,jvm根据自己的内存模型会把内存分为:方法区 Method Area,虚拟机栈VM Stack,本地方法栈Native Method Stack,堆Heap,程序计数器 Program Counter Register

    方法区

    是被所有的线程共享的一块内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    虚拟机栈

    每个方法执行的同时会创建一个栈帧,栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

    本地方法栈

    本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法服务(也就是字节码),而本地方法栈为虚拟机使用到的Native方法服务。

    堆Heap

    Java堆是被所有的线程共享的一块内存区域,在虚拟机启动时创建。

    程序计数器

    程序计数器可以看做是当前线程所执行的字节码的行号指示器

    详细参考:https://www.jianshu.com/p/7ebbe102c1ae

    volatile数据一致性

    从方法区定义可知一个变量count声明为volatile类型,则意味这这个变量将存储在方法区;而线程运行时,线程会单独拷贝一份count变量到自己的内存区域,这个内存区域也就是虚拟机栈,画个图
    在这里插入图片描述
    每个线程都拷贝了一份count变量到自己的内存区域,那是怎么做到一致性的?

    在多线程情况下,如果某一个线程要对volatile 变量修改,首先运行该线程的cpu会锁定方法区变量内存区域,保证做到数据修改的原子性;那现在问题就来了,其他线程是怎么获得最新的数据?运行其他线程的cpu有会嗅探总线上该内存区域的地址是否被修改;如果该线程缓存的变量副本所指向方法区的地址发生了变化,则将自己缓存的数据设置成无效,等待线程使用到该变量的时候,会从新从方法区获得一份新的数据,同步到本地线程的缓存区域,这样就volatile变量就做到了多线程的可见以及一致性

    展开全文
  • Volatile 原理

    2020-12-15 19:19:30
    Volatile 原理 volatile 的底层实现原理是内存屏障,Memory Barrier 对 volatile 变量的写指令后会加入 写屏障 对 volatile 变量的读指令前会加入 读屏障 1. 如何保证可见性 写屏障保证在该屏障之前的,对共享...

    Volatile 原理

    volatile 的底层实现原理是内存屏障,Memory Barrier

    • 对 volatile 变量的写指令后会加入 写屏障
    • 对 volatile 变量的读指令前会加入 读屏障

    1. 如何保证可见性

    • 写屏障保证在该屏障之前的,对共享变量的改动,都同步到主存当中
    public void actor2() {
    	num = 2;
    	// volatile 变量
    	ready = true;
    	// 写屏障
    }
    
    • 而读屏障保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据
    public void actor1() {
    	// 读屏障
    	if(ready) {
    		
    	}
    }
    

    2. 如何保证有序性

    • 写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后
    public void actor2() {
    	num = 2;
    	// volatile 变量
    	ready = true;
    	// 写屏障
        int i = 5; // 不会被重排序到 ready 前面
        int j = 3;
    }
    
    • 读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前
    public void actor1(int i) {
    	// 读屏障
    	if(ready) {
    		i = 5;
    	}
    }
    

    double-checked locking 问题

    public final class Singleton {
    	private Singleton() {}
        // 使用 volatile 可以避免多线程问题
        private static Singleton INSTANCE = null;
        public static Singleton getInstance () {
            if(INSTANCE == null) {
                synchronized(Singleton.class) {
                    if(INSTANCE == null) {
                        INSTANCE = new Singleton();
                    }
                }
            }
            return INSTANCE;
        }
    }
    

    字节码

    0: getstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;
    3: ifnonnull 37
    6: ldc #3 // class cn/itcast/n5/Singleton
    8: dup
    9: astore_0
    10: monitorenter
    11: getstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;
    14: ifnonnull 27
    17: new #3 // class cn/itcast/n5/Singleton
    20: dup
    21: invokespecial #4 // Method "<init>":()V
    24: putstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;
    27: aload_0
    28: monitorexit
    29: goto 37
    32: astore_1
    33: aload_0
    34: monitorexit
    35: aload_1
    36: athrow
    37: getstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;
    40: areturn
    

    也许 jvm 会优化为 先执行 24, 再执行21。在多线程环境下,就会出现获取到一个未初始化完毕的单例。(volatile 底层是 通过 插入 一条 lock 指令实现)

    final 原理

    设置 final 变量的原理

    理解了 volatile 原理,再对比 final 的实现就比较简单了

    public class TestFinal {
    	final int a = 20;
    }
    
     0 aload_0
     1 invokespecial #1 <java/lang/Object.<init>>
     4 aload_0
     5 bipush 20
     7 putfield #2 <mythread/finaldoc/TestFinal.a>
         // 内存屏障
    10 return
    
    
    展开全文
  • Volatile原理

    2018-11-07 15:10:52
    1、volatile的实现可见性原理 volatile的是通过加入内存屏障和禁止指令重排序优化来实现的。 对于写而言:volatile关键修饰的变量在被写操作时,会在写操作后加入一条store指令,将当前工作内存中的数据刷新到...

    1、volatile的实现可见性原理

    volatile的是通过加入内存屏障和禁止指令重排序优化来实现的。

    对于写而言:volatile关键修饰的变量在被写操作时,会在写操作后加入一条store指令,将当前工作内存中的数据刷新到主存中去。

    对于读而言:volatile关键修饰的变量在被读操作时,会再读操作前加入一条load执行,将主存中的数据更新到当前工作内存中。

    线程写volatile变量的过程:

    1、改变线程工作内存中的volatile变量副本的值;

    2、将修改后的副本变量更新到主内存中去。

    线程读volatile变量的过程:

    1、先从主内存中将最新volatile变量的最新值读取到当前线程的工作内存中;

    2、线程从工作内存中获取volatile变量副本的值。


    2、volatile不能保证原子性

    如下代码:

    public static void main(String[] args) {
        int number = 1;
        number++;
        System.out.println(number);
    }

    在这段代码中,number++是一段线程不安全的代码,这一行代码可以拆分成如下三步操作:

    1、读取number的值

    2、将number的值加1

    3、将加后的number写回内存中

    要想实现原子性使用synchronized关键字是可以实现的

    synchronized (this) {
        number++;
    }

    如果为number变量增加上volatile关键字显然无法实现原子性。

    代码:

    public class VolatileDemo3 {
    
        private volatile int number = 0;
    
        private Lock lock = new ReentrantLock();
    
        public int getNumber() {
            return this.number;
        }
    
    //    public synchronized void increase() {
        public void increase() {
    //        try {
    //            Thread.sleep(100);
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        }
    
    //        lock.lock();
    //        try {
                this.number++;
    //        } finally {
    //            lock.unlock();
    //        }
        }
    
        public static void main(String[] args) {
            int a = 1;
            long startTime = System.currentTimeMillis();
            for (;;) {
                int i = volatileTest(a);
                a ++;
                if (i < 500) {
                    System.out.println("在第" + a + "次,获得的结果为" + i);
                    break;
                } else if(i >= 10000) {
                    System.out.println("已经十万次,获得结果为" + i);
                    break;
                }
            }
            long endTime = System.currentTimeMillis();
    
            System.out.println("共耗时:" + (endTime - startTime) / 1000);
        }
    
        public  static int volatileTest(int a) {
            VolatileDemo3 volatileDemo3 = new VolatileDemo3();
            long startTime = System.currentTimeMillis();
            for (int i = 0;i < 500;i++) {
    
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        volatileDemo3.increase();
                    }
                }).start();
            }
            /**
             * 打印当前线程列表
             */
    //        Thread.currentThread().getThreadGroup().list();
            /**
             * 这个循环的意思是当所以线程还有大于1个线程在执行,主线程都让出执行权,当所有线程执行完程序才向下执行
             * 这里在idea开发工具里面需要设置大于2,在eclipse里面只需要设置1就行了,因为idea还有一个监控线程
             */
            while(Thread.activeCount() > 2) {
                Thread.yield();
            }
    
            long endTime = System.currentTimeMillis();
    
            System.out.println("第"+ a +"循环启用500个线程,共耗时:" + (endTime - startTime));
    
    
            return volatileDemo3.number;
        }
    }

    上面的代码是启动循环,在循环中启动500个线程同时执行++的操作,由于++是线程不安全的,且volatile无法保证原子性,在循环过程中可能出现小于500的情况,主要原因可以分析下number++的原理

    number++线程不安全的情况:

    1、线程1获得CPU执行权,需要执行++操作,发现number是volatile修饰的,先从主存中获取number的最新值为0到当前线程工作内存中;

    2、此时,线程2获得CPU执行权,线程1进入等待状态,线程2发现number是volatile修饰的,先从主存中获取number的最新值为0到当前线程工作内存中;

    3、线程2执行了++操作,并且将number的值更新到主存中,此时线程2的工作内存和主存的number都为1;

    4、线程1重新获得执行权,将线程1工作内存中的number值加1后,并刷新到主存中,由于线程1的工作内存还为0,所以刷新到主存后主存的结果为1

    这样就出现了加了两次,但是结果还是1的情况。

    结果方案:

    1、使用Synchronized关键字

    使用关键字有两种形式,一个是锁方法,另外一个是锁代码块

    方法1:

    public synchronized void increase() {
        this.number++;
    }

    方法2:

    public void increase() {
        synchronized (this) {
            this.number++;
        }
    }

    2、使用Lock类

    这种方式是使用jdk1.5中的ReentrantLock类

    private Lock lock = new ReentrantLock();
    public synchronized void increase() {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.lock();
            try {
                this.number++;
            } finally {
                lock.unlock();
            }
    }

    3、使用AtomicInteger

    AtomicInteger这个类是在jdk1.5以后出现的一个原子性加减的类,使用方式如下:

    private AtomicInteger atomicInteger = new AtomicInteger(0);
    atomicInteger.getAndIncrement();//实现++操作
    atomicInteger.get();//获取当前值

     

     

     

     

     

     

    展开全文
  • volatile 原理

    千次阅读 2018-03-22 09:41:16
    &nbsp;&nbsp;&nbsp;&nbsp;定义:Java 允许线程访问共享变量,为了确保共享...有 volatile 修饰的共享变量进行写操作时,它的汇编指令中会有 lock 字样,称为 Lock 前缀。Lock 前缀的指令在多核处...

        定义:Java 允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。

    volatile 如何确保共享变量的可见性?
        有 volatile 修饰的共享变量进行写操作时,它的汇编指令中会有 lock 字样,称为 Lock 前缀。Lock 前缀的指令在多核处理器下会引发两件事情:
    1)将当前处理器缓存行的数据写回到系统内存
    2)这个写回内存的操作会使其他 CPU 里缓存了该内存地址的数据无效

    0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);

        解释:如果对声明了 volatile 的变量进行写操作时,JVM 会向处理器发送一条 Lock 前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是,其他处理器的缓存行内的数据仍然是旧数据。因此为了保证各个处理器的缓存是一致的,就要采用缓存一致性协议,即每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改了,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作时,会重新从系统内存中读取数据到缓存行中。

    展开全文
  • volatile原理分析

    2020-12-02 17:18:34
    volatile原理分析: 一:代码实例(不加volatile): static volatile boolean initFlag=false; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @...
  • 浅淡volatile原理

    2018-08-20 17:10:00
    浅淡volatile原理 Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性” Volatile的官方定义 Java语言规范第三版中对volatile的定义如下: java编程语言允许线程访问...
  • 主要介绍了Java多线程volatile原理及用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • Java volatile原理浅析

    2020-11-24 21:22:32
    Java volatile原理浅析 volatile的定义与应用 volatile的通俗含义是共享变量的一致性,即在一个线程更改了该共享变量的值的时候,其他的线程能立马知道改动的发生,并且获取到新值,这一性质也可以称为“可见性”。 ...
  • volatile原理及其特性

    2019-12-23 20:34:19
    volatile原理 volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。在JVM底层volatile是采用“内存屏障”来实现的。观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入...
  • 面试题:请你说下volatile这个关键字,volatile原理是什么?面试题:请你说下volatile这个关键字,volatile原理是什么?题目分析题目回答volatile的作用volatile的原理可见性实现有序性实现 面试题:请你说下...
  • volatile原理与技巧

    2017-02-08 17:52:53
    为什么使用volatile比同步代价更低?...volatile原理是什么? volatile的语义, 其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我.(工作内存详见Java内存模型) 因此, 当多核或多线程在访问该变量时,
  • 多线程之volatile原理分析 volatile与有序性 volatile一个强大的功能,那就是他可以禁止指令重排优化。通过禁止指令重排优化,就可以保证代码程序会严格按照代码的先后顺序执行。那么volatile又是如何禁止指令重排的...
  • Java并发1:volatile原理

    2020-08-08 21:11:00
    2、volatile原理 volatile 的底层实现原理是内存屏障,Memory Barrier(Memory Fence) volatile 变量的写指令后会加入写屏障 对 volatile 变量的读指令前会加入读屏障 3、如何保证可见性 写屏障保证在该屏障之前...
  • 1. volatile 原理 Volatile 是轻量级的synchronized,保证了共享变量的可见性(一个线程修改共享变量,另一个变量能读到这个修改的值。),volatile不会引起线程上下文的切换和调度,所以比synchronized执行成本低...
  • Volatile原理 - 2.1 内存可见性保证 - 2.2 禁止重排序 - 2.2.1 为什么重排序会影响volatile相关变量的结果? - 2.2.2 内存屏障 - 3. volatile重排序规则 概要 ...
  • volatile是java语言中的一个关键字,常用于并发编程,有两个...volatile原理 介绍其可见性先从cpu,cpu缓存和内存的关系入手. cpu缓存是一种加速手段,cpu查找数据时会先从缓存中查找,如果不存在会从内存中查找,所以如...
  • 深入理解volatile原理与使用Volatile称之为轻量级锁,被volatile修饰的变量,在线程之间是可见的,保证不了非原子性操作,它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。...
  • java volatile 原理

    2018-11-16 15:20:02
    一、介绍 volatile 是轻量级的 synchrocized,, 它再多处理器开发中保证了共享变量“可见性”。 可见性:当一个线程修改一个...二、volatile 的定义和原理 如果一个字段呗声明成volatile,java线程内存模型确保所有...
  • volatile原理解析

    2021-02-22 11:02:11
    那么 我来回答一下volatile, 修饰变量,可以确保在 高并发的代码中,取得 变量值 始终是,是最新得,实现原理,如下 被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象 ...
  • Volatile原理解析

    2020-08-20 16:17:13
    一、实现原理     对于volatile的解释,我相信更直白的说就是对于一个被volatile关键字修饰的变量,在并发情况下Java内存模型(JMM)保证每个线程对该变量的可见性,保证他们读取的数据是一致的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 90,120
精华内容 36,048
关键字:

volatile原理