java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java Volatile变量

Java并发编程之Volatile变量详解分析

作者:该用户快成仙了

Volatile关键字是Java提供的一种轻量级的同步机制,本篇文章深入浅出的讲讲Java并发编程的Volatile,通读本篇对大家的学习或工作具有一定的价值,需要的朋友可以参考下

Volatile关键字是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量, 相比synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。 但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其使用也更容易出错。

一、volatile变量的特性

1.1、保证可见性,不保证原子性

来看一段代码:

public class Test {
    public static void main(String[] args) {
        WangZai wangZai = new WangZai();
        wangZai.start();
        for(; ;){
            if(wangZai.isFlag()){
                System.out.println("hello");
            }
        }
    }
 
    static class WangZai extends Thread {
 
        private boolean flag = false;
 
        public boolean isFlag(){
            return flag;
        }
 
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            System.out.println("flag = " + flag);
        }
    }
}

你会发现,永远都不会输出hello这一段代码,按道理线程改了flag变量,主线程也能访问到的呀?

但是将flag变量用volatile修饰一下,就能输出hello这段代码

private volatile boolean flag = false;

每个线程操作数据的时候会把数据从主内存读取到自己的工作内存,如果他操作了数据并且写会了,那其他已经读取的线程的变量副本就会失效了,需要对数据进行操作又要再次去主内存中读取了。

volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值。

1.2、禁止指令重排

重排序需要遵守一定规则:

什么是重排序?

为了提高性能,编译器和处理器常常会对既定的代码执行顺序进行指令重排序。

重排序的类型有哪些呢?

深入浅出谈谈Java并发编程:Volatile

一个好的内存模型实际上会放松对处理器和编译器规则的束缚,也就是说软件技术和硬件技术都为同一个目标,而进行奋斗:在不改变程序执行结果的前提下,尽可能提高执行效率。

JMM对底层尽量减少约束,使其能够发挥自身优势。

因此,在执行程序时,为了提高性能,编译器和处理器常常会对指令进行重排序。

一般重排序可以分为如下三种:

那 Volatile 是怎么保证不会被执行重排序的呢?

二、内存屏障

java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。

为了实现volatile的内存语义,JMM会限制特定类型的编译器和处理器重排序,JMM会针对编译器制定volatile重排序规则表:

是否能重排序第二个操作第一个操作普通读/写volatile读volatile写普通读/写NOvolatile读NONONOvolatile写NONO

举例来说,第三行最后一个单元格的意思是:在程序顺序中,当第一个操作为普通变量的读或写时,如果第二个操作为volatile写,则编译器不能重排序这两个操作。

从上表我们可以看出:

需要注意的是:volatile写是在前面和后面分别插入内存屏障,而volatile读操作是在后面插入两个内存屏障。

深入浅出谈谈Java并发编程:Volatile

深入浅出谈谈Java并发编程:Volatile

从JDK5开始,提出了happens-before的概念,通过这个概念来阐述操作之间的内存可见性。

三、happens-before

happens-before 关系的定义:

看到这儿,你是不是觉得,这个怎么和 as-if-serial 语义一样呢。没错, happens-before 关系本质上和 as-if-serial 语义是一回事。

as-if-serial 语义保证的是单线程内重排序之后的执行结果和程序代码本身应该出现的结果是一致的,

happens-before 关系保证的是正确同步的多线程程序的执行结果不会被重排序改变。

一句话来总结就是:如果操作 A happens-before 操作 B ,那么操作 A 在内存上所做的操作对操作 B 都是可见的,不管它们在不在一个线程。

在 Java 中,对于 happens-before 关系,有以下规定:

到此这篇关于Java并发编程之Volatile变量详解分析的文章就介绍到这了,更多相关Java Volatile变量内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文