深入理解volatile原理与使用
volatile:称之为轻量级锁,被volatile修饰的变量,在线程之间是可见的。
可见:一个线程修改了这个变量的值,在另一个线程中能够读取到这个修改后的值。
synchronized除了线程之间互斥之外,还有一个非常大的作用,就是保证可见性。以下demo即保证a值的可见性。
首先来看demo:
package com.roocon.thread.t7;
public class Demo {
    private int a = 1;
    public int getA() {
        return a;
    }
    public void setA(int a) {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.a = a;
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
               demo.setA(10);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(demo.getA());
            }
        }).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("最终结果:" + demo.getA());
    }
}
运行结果:
1
最终结果:10
解释:线程1执行set操作,但是,线程2可能在线程1执行set操作成功之前就进行了get操作,这样,get得到的仍然是修改之前的a值
那么,如何保证线程1在执行set操作时,其他线程得到的a值是线程1修改后的值呢?采用同步锁可以实现效果。同步方法上锁的是同一个实例,因此,在执行set方法前,线程1获取了实例锁,那么,其他线程在执行get方法时,必须获得同一把实例锁才可以得到a的值,所以,必须等待线程1释放实例锁后,其他线程才可以继续执行同步的get方法。这样,就可以保证其他线程得到的a值一定是线程1修改后的值。
package com.roocon.thread.t7;
public class Demo {
    private int a = 1;
    public synchronized int getA() {
        return a;
    }
    public synchronized void setA(int a) {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.a = a;
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
               demo.setA(10);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(demo.getA());
            }
        }).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("最终结果:" + demo.getA());
    }
}
运行结果:
10
最终结果:10
当然,要清楚的一点是,这里的输出统一仅仅是基于线程1的set操作是先于线程2的get操作执行的。但是,要知道,两个线程并发执行,不一定是set操作一定优先get操作执行的。
同样的,volatile也可以保证可见性。因为synchronized是重量级锁,所以,使用volatile会更好。
package com.roocon.thread.t7;
public class Demo {
    private volatile int a = 1;
    public int getA() {
        return a;
    }
    public void setA(int a) {
        this.a = a;
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
               demo.setA(10);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(demo.getA());
            }
        }).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("最终结果:" + demo.getA());
    }
}
运行结果:
10
最终结果:10
再来看个demo理解volatile的可见性:
package com.roocon.thread.t7;
public class Demo2 {
    public volatile boolean run = false;
    public static void main(String[] args) {
        Demo2 demo2 = new Demo2();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i< 5; i++) {
                    System.out.println("执行了第" + i +"次");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                demo2.run = true;
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(!demo2.run){
                }
                System.out.println("线程2执行了");
            }
        }).start();
    }
}
运行结果:
执行了第1次
执行了第2次
执行了第3次
执行了第4次
线程2执行了
这里需要注意的是,volatile只能保证线程的可见性,但是并不能保证原子性操作。如果volatile修饰的变量涉及到非原子操作,那么,我们需要使用synchronized来保证它的安全性。
package com.roocon.thread.t7;
public class Demo {
    private volatile int a = 1;
    public synchronized int getA() {
        return a++;
    }
    public synchronized void setA(int a) {
        this.a = a;
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
               demo.setA(10);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(demo.getA());
            }
        }).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("最终结果:" + demo.getA());
    }
}
运行结果:
10
最终结果:11
volatile:
在多处理器的系统上,它会执行一下步骤:
1.将当前处理器缓存行的内容写回到系统内存
2.这个写回到内存的操作会使得在其他CPU里缓存了该内存地址的数据失效
3.其他CPU缓存数据失效,则会重新去内存中读取值,也就是被修改的数据
这里要理解的一个概念是缓存行,缓存行是CPU缓存的一个基本单位。
硬盘---内存--CPU缓存,内存的读取速度比硬盘快,CPU缓存的读取速度比内存更高效。
volatile和synchronized的比较:
synchronized是完全可以替换volatile的,只是volatile相对synchronized是轻量级锁。
volatile是不可以完全替换synchronized的,因为volatile只能保证可见性,并不能保证操作的原子性。所以,很多情况下,两者会相互结合使用。
深入理解volatile原理与使用的更多相关文章
- Java并发编程原理与实战十二:深入理解volatile原理与使用
		volatile:称之为轻量级锁,被volatile修饰的变量,在线程之间是可见的. 可见:一个线程修改了这个变量的值,在另一个线程中能够读取到这个修改后的值. synchronized除了线程之间互 ... 
- 并发编程-CPU执行volatile原理探讨-可见性与原子性的深入理解
		volatile的定义 Java语言规范第3版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量.Jav ... 
- 从缓存行出发理解volatile变量、伪共享False sharing、disruptor
		volatilekeyword 当变量被某个线程A改动值之后.其他线程比方B若读取此变量的话,立马能够看到原来线程A改动后的值 注:普通变量与volatile变量的差别是volatile的特殊规则保证 ... 
- 5.彻底理解volatile
		1. volatile简介 在上一篇文章中我们深入理解了java关键字synchronized,我们知道在java中还有一大神器就是关键volatile,可以说是和synchronized各领风骚,其 ... 
- 彻底理解volatile,领悟其中奥妙
		本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ... 
- 让你彻底理解volatile,面试不再愁
		本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ... 
- 深入理解volatile关键字
		Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作内存 ... 
- volatile原理和应用场景
		volatile是java语言中的一个关键字,常用于并发编程,有两个重要的特点:具有可见性,java虚拟机实现会为其满足Happens before原则;不具备原子性.用法是修饰变量,如:volati ... 
- 彻底理解volatile关键字
		1. volatile简介 在上一篇文章中我们深入理解了java关键字,我们知道在java中还有一大神器就是关键volatile,可以说是和synchronized各领风骚,其中奥妙,我们来共同探讨下 ... 
随机推荐
- PHP7预编译mysqli查询操作
			//连接数据库 $mysqli = new mysqli("localhost", "root", "root", "mobile ... 
- C++线程同步之事件(生产者与消费者问题)
			#include <windows.h> #include <stdio.h> HANDLE g_hSet = NULL; HANDLE g_hClear = NULL; HA ... 
- Python线性回归算法【解析解,sklearn机器学习库】
			一.概述 参考博客:https://www.cnblogs.com/yszd/p/8529704.html 二.代码实现[解析解] import numpy as np import matplotl ... 
- springboot学习入门简易版六---springboot2.0整合全局捕获异常及log4j日志(12-13)
			使用Aop实现 1创建异常请求 在原有项目基础上,jspController中创建一个可能发生异常的请求: /** * 全局捕获异常测试 * @param i * @return */ @Reques ... 
- Android笔记(四十六) Android中的数据存储——XML(二)PULL解析
			PULL 的工作原理: XML pull提供了开始元素和结束元素.当某个元素开始时,可以调用parser.nextText()从XML文档中提取所有字符数据.当解析到一个文档结束时,自动生成EndDo ... 
- 17.centos7基础学习与积累-003-命令练习01
			1.从头开始积累centos7系统运用 大牛博客:https://blog.51cto.com/yangrong/p5 linux命令的学习: 创建目录:mkdir mkdir /data mkdir ... 
- 在vue中使用jsx语法
			什么是JSX? JSX就是Javascript和XML结合的一种格式.React发明了JSX,利用HTML语法来创建虚拟DOM.当遇到<,JSX就当HTML解析,遇到{就当JavaScript解 ... 
- zabbix-web切换为nginx及https
			目录 zabbix-web切换为nginx及https 1.背景和环境 2.安装nginx 2.1.编译参数 2.2.修改配置文件并配置https 2.3.配置nginx为系统服务 3.安装php 3 ... 
- Redis锁机制的几种实现方式
			1. redis加锁分类 redis能用的的加锁命令分表是INCR.SETNX.SET 2. 第一种锁命令INCR 这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执 ... 
- Java精通并发-死锁检测与相关工具详解
			关于死锁其实在之前https://www.cnblogs.com/webor2006/p/10659938.html的jvm学习中已经详细举过例子了,不过这里再来复习一下,另外是从并发这个专题领域的角 ... 
