Volatile的学习
首先先介绍三个性质
可见性
可见性代表主内存中变量更新,线程中可以及时获得最新的值。
下面例子证明了线程中可见性的问题
由于发现多次执行都要到主内存中取变量,所以会将变量缓存到线程的工作内存,这样当其他线程更新该变量的时候,该线程无法得知,导致该线程会无限的运行下去。
public class test1 {
private static int flag = 1;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
while (flag == 1){
}
},"t1");
t1.start();
Thread.sleep(1000);
flag = 2;
}
}
疑问
当我们在这个死循环中加入一个synchronized关键字的话就会将更新
猜测:synchronized会使更新当前线程的工作内存
public class test1 {
private static int flag = 1;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
while (flag == 1){
synchronized ("1"){ }
}
},"t1");
t1.start();
Thread.sleep(1000);
flag = 2;
}
}
原子性
即多线程中指令执行会出现交错,导致数据读取错误。
比如i++的操作就可以在字节码的层面可以被看成以下操作
9 getstatic #9 <com/zhf/test3/test2.i : I> 获得i
12 iconst_1 将1压入操作数栈
13 isub 将两数相减
14 putstatic #9 <com/zhf/test3/test2.i : I> 将i变量存储
然后在多线程的情况下,会出现以下程序出现非0的结果。
public class test2 {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int j = 0; j < 400; j++) {
i++;
}
});
Thread t2 = new Thread(()->{
for (int j = 0; j < 400; j++) {
i--;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
设计模式---两阶段停止使用volatile
@Slf4j
public class test3 {
private Thread monitor;
private volatile boolean flag = false;
public static void main(String[] args) {
test3 test3 = new test3();
test3.monitor = new Thread(()->{
while (true){
Thread thread = Thread.currentThread();
if (test3.flag){
log.debug("正在执行后续");
break;
}
try {
log.debug("线程正在执行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
// 当进程在睡眠过程中被Interrupte()打断此时isInterrupted()为false
// 从而当异常被抓住后会继续执行
// 所以要调用下面方法继续将isInterrupted()置为true
// thread.interrupt();
}
}
});
test3.start();
try {
Thread.sleep(5500);
} catch (InterruptedException e) {
e.printStackTrace();
}
test3.stop();
}
public void stop(){
flag = true;
monitor.interrupt();
}
public void start(){
monitor.start();
}
}
设计模式---犹豫模式
具体实现,最常见的就是单例模式。
首先是饿汉模式,这里的多线程安全是由JVM保证的,对象是在类加载的加载阶段创建的。
class SingtonHungry{
private static Object object = new Object();
// 饿汉模式
public synchronized Object getObject() {
return object;
}
}
其次就是饿汉模式,最常见的不过就是下面的进行多线程安全的方案。文章后面会对其进行优化。
class SingtonLazy{
private Object object;
// 懒汉模式
// 由于这样的话不管有没有创建出对象都要加锁然后才能取对象,性能太差
public synchronized Object getObject() {
if (object == null){
object = new Object();
return object;
}
return object;
}
}
有序性
JVM会对指令进行重排序,其和CPU的流水线操作类似,当需要流水线操作的时候,需要进行优化的时候,就会对CPU指令进行重排序优化。
当操作的顺序变了之后,就会出现问题。可能会导致条件的提前触发等等。
Volatile使用
使用域: Volatile只能在类的静态成员变量或者成员变量上。
volatile标识符能够让线程强制去读主存的该变量的值,保证了线程变量的可见性。
volatile标识符能够让线程去顺序执行该变量的操作,保证了执行变量的语句的有序性
在读取该变量时,会为其添加读屏障。在该读屏障之后的代码不会放在读屏障之前执行。
在写该变量时,会为其添加写屏障。在该写屏障之前的代码不会在屏障之后执行。
所以在volatile的修饰下,能够保证变量的可见性和有序性,但并不能保证其的原子性。
class SingtonLazy{
// 加上volatile的主要目的就是防止在synchronized内的代码指令重排,正常是先构造好对象然后赋对象地址
// 导致object会被首先赋予了地址,导致其不为null,然而构造方法还没有开始构造
// 被其他的线程拿走会出现使用出错。
private static volatile Object object;
// 懒汉模式
public static Object getObject() {
if (object != null){
return object;
}else{
// 这里可能出现这里的线程还没有为其进行声明对象,但已经由线程进入了等待锁
// 所以需要在这里来一个为空判断。
synchronized (SingtonLazy.class){
// 这里可能会出现指令重排,所以要加上volatile
if(object == null){
object = new Object();
}
return object;
}
}
}
}
实现单例的另外一个方式
public class Singleton {
// 当使用到ObjectHolder才会进行到这个静态内部类的加载,同时才会创建该类
// 也是属于懒汉式
private static class ObjectHolder{
static final Singleton singleton = new Singleton();
}
}
synchronized补充
首先在synchronized代码块中,它会保证代码块中的可见性,原子性和有序性。
有序性仅仅是表现在synchronized的执行后最后的结果都是一样的,并不会阻止JVM在其内部进行代码的重排序。就比如上个例子来说,在synchronized代码块中最后代码的执行结果都是一样的,但可能由于其优化,导致其他线程出错。
Volatile的学习的更多相关文章
- volatile关键字 学习记录2
public class VolatileTest2 implements Runnable{ volatile int resource = 0; public static void main(S ...
- volatile关键字 学习记录1
虽然已经工作了半年了...虽然一直是在做web开发....但是平时一直很少使用多线程..... 然后最近一直在看相关知识..所以就有了这篇文章 用例子来说明问题吧 public class Volat ...
- C++中函数的默认参数和C语言中volatile的学习
1.函数默认参数 1 int func(int a,int b=10) 2 { 3 return a*b; 4 } 5 6 int main() 7 { 8 int c=func(2); 9 cout ...
- volatile关键字学习
volatile关键字在实际工作中我用的比较少,可能因为我并不是造轮子的.但是用的少不是你不掌握的借口,还是要创造场景去使用这个关键字,本文将会提供丰富的demo. volatile 发音:英[ˈvɒ ...
- 面经手册 · 第14篇《volatile 怎么实现的内存可见?没有 volatile 一定不可见吗?》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.码场心得 你是个能吃苦的人吗? 从前的能吃苦大多指的体力劳动的苦,但现在的能吃苦已经包括太 ...
- C++——volatile关键字的学习
首先声明一点,本文是关于volatile关键字的学习,学习内容主要是来自一些大牛的网络博客. 一篇是何登成先生的C/C++ Volatile关键词深度剖析(http://hedengcheng.com ...
- JAVA多线程基础学习三:volatile关键字
Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字. volatile,从字面上说是易变的.不稳定的,事实上,也确实如此,这个 ...
- Java多线程学习(三)volatile关键字
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79680693 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- 【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile
章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译= ...
随机推荐
- 什么是 Spring Framework?
Spring 是一个开源应用框架,旨在降低应用程序开发的复杂度.它是轻量级.松 散耦合的.它具有分层体系结构,允许用户选择组件,同时还为 J2EE 应用程序 开发提供了一个有凝聚力的框架.它可以集成其 ...
- Azure DevOps (八) 通过流水线编译Docker镜像
上一篇文章我们完成了最简单的传统部署:上传应用到服务器上使用守护进程进行应用的部署. 本篇文章我们开始研究容器化和流水线的协作. 在开始操作之前,我们首先需要准备一下我们的dockerfile,这里我 ...
- Cloud Design Patterns & Architecture Styles
Cloud Design Patterns Categories Data Management Design and Implementation Messaging Patterns Ambass ...
- 从零开始画自己的DAG作业依赖图(四)--节点连线优化版
概述 上个版本简单的连线在一些复杂场景,尤其层级比较多,连线跨层级比较多的情况下,会出现线条会穿过矩形的情况,这一讲就是在这个基础上,去优化这个连线. 场景分析 在下面几种情况下,简单版本的画法已经没 ...
- 一套Vue的单页模板:N3-admin
趁着周末偷来一点闲,总结近期的工作和学习,想着该花点心思把N3-admin这套基于N3-components的单页应用模板简单的给介绍一下. 首发于个人博客:blog.lxstart.net项目路径: ...
- 创建axios拦截器
上一篇说axios并发的时候有提到 axios的请求统一管理是为了创建拦截器 具体说一下拦截器的创建 import Vue from 'vue'; import axios from 'axios'; ...
- 【网易云信】H5 容器技术方案
Native 开发原生应用是手机操作系统厂商(目前主要是苹果的 iOS 和 Google 的 Android)对外界提供的标准化的开发模式,他们对于 Native 开发提供了一套标准化实现和优化方案. ...
- 快如闪电,触控优先。新一代的纯前端控件集 WijmoJS发布新版本了
全球最大的控件提供商葡萄城宣布,新一代纯前端控件 WijmoJS 发布2018 v1 版本,进一步增强产品功能,并支持在 Npm 上的安装和发布,极大的提升了产品的易用性. WijmoJS 是用 Ty ...
- 假期任务一:安装好JAVA开发环境并且在Eclipse上面成功运行HelloWorld程序
(本周主要做了java环境的安装,安装完jdk后又安装了eclipse,平均每天两小时Java吧,这周敲代码的时间比较少,大多是在b站看java入门视频和菜鸟教程的基础语法,也就打开eclipse验证 ...
- python 简要小结
初学python 简单总结部分内置函数 将两个数组合并为元组:zip() 解压:zip(*zip) range(a,b,c) 取值范围 起始:a 结尾:b 间隔:c (参数不能为空否则 ...