《volatile使用与学习总结:2023-12-17》多层面分析学习java关键字--volatile

2023-12-18 17:36

本文主要是介绍《volatile使用与学习总结:2023-12-17》多层面分析学习java关键字--volatile,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、简介

volatile是java中提供的一种轻量级的同步机制,相比于synchronized更加轻量,因为volatile避免了线程频繁的上下文切换和调度,但是也有缺点,同步性差并且使用起来易出错。

二、并发编程的三个概念

  1. 原子性
    原子性即一个操作或者多个操作要不全部执行完(执行过程不被干扰),要不都不执行。
  2. 可见性
    可见性指的是,当一个线程操作一个变量的值时,在其他线程是可以及时看到这个变量修改后的值,并能获取来操作。对比与synchronized和lock,这两者也都能保证可见性,因为它们在每次释放锁之前都会将修改好的变量值更新到主内存中,保证了可见性。
  3. 有序性
    即程序执行的顺序是按照代码的先后顺序来执行的。
    java内存模型中所说的有序性总结为,在本线程中看都是有序执行的,在一个线程看另外一个线程都是无序的,也就是说前半句表示“线程内表现为串型语义”,后面句表示“指令重排序”现象和“主内存与工作内存同步延迟”现象。
    Java的内存模型

三、锁的互斥和可见性

  1. 互斥:即一次只允许一个线程持有某个特定的锁,而且一次只允许一个线程访问共享数据
  2. 锁的可见性:在前一个线程释放锁之前对变量进行更改后,将更改后的变量由工作内存更新到主内存中,保证下一个获得该锁的线程对这个变量具有可见性,如果没有及时更新到主内存中后果很严重,会导致数据不一致问题。
    要让volatile线程安全,必须保证两点
    (1)对变量的写操作不依赖于当前值
    (2)该变量没有包含在具有其他变量的变式中
    事实上就是保证该变量操作的原子性。

四、volatile变量的特性

  1. 保证可见性,不保证原子性
    (1)当写一个变量是,当前线程会将该变量从工作内存强制更新到主内存,保证其他线程实时获取的变量是最新的,保证了可见性。
    (2)这个写操作会导致其他线程中这个变量缓存无效。
  2. 禁止指令重排序
    重排序是程序为了更好地性能调度,对指令进行新的排序的一种手段,需要遵守以下规则
    (1)重排序不会对存在数据依赖关系的程序重排序
    (2)重排序是为了优化性能,但无论怎么排序,在单线程下,从排序后的运算结果都不会改变,但是在多线程下重排序则可能会影响结果
    (3)使用volatile能够禁止指令重排序,但要遵守一些规则。
    1)当执行到volatile修饰的变量进行读或者写时,该操作前面的指令都执行完毕,且该操作结果对后面指令可见,而且该操作后面的指令都未执行
    2)当指令优化时,不能将指令放到volatile后面执行,也不能把把volatile后面的语句放到前面执行。

五、volatile不适合的场景

  1. volatile不适合复合操作,下面模拟十个线程对num自增,每个线程自增1000,
    比如 num++,不是一个原子性操作,分为读取,加,赋值三个操作,所以结果达不到10000
public class VolatileTest1 {private static volatile int num = 0;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10; i++) {Thread thread = new Thread(() -> {for (int j = 0; j < 1000; j++) {num++;}});thread.start();}System.out.println("num=" + num);}
}

结果
num=5657
num=4414
num=7491
…(完全随机)

  1. 解决办法
    (1)使用synchornized锁住变量所在的类
public class VolatileTest1 {public static volatile int num = 0;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 10000; j++) {synchronized (VolatileTest1.class) {num++;}}}}).start();}// 保证线程执行完毕while (Thread.activeCount() > 1) {Thread.yield();}System.out.println("num=" + num);}
}

结果:
num=100000

(2)使用lock锁住该方法

public class VolatileTest3 {public static volatile int num = 0;public static Lock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 10000; j++) {lock.lock();try {num++;}finally {lock.unlock();}}}}).start();}// 保证线程执行完毕while (Thread.activeCount() > 1) {Thread.yield();}System.out.println("num=" + num);}
}

结果:
num=100000

(3)使用并发原子操作类AtomicInteger ,其原理是通过CAS循环的方式来保证原子性

public class VolatileTest5 {public static volatile AtomicInteger num = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 10000; j++) {num.getAndIncrement();}}}).start();}// 保证线程执行完毕while (Thread.activeCount() > 1) {Thread.yield();}System.out.println("num=" + num);}
}

结果:
num=100000

六、单例模式的双重检验锁为什么要加volatile

public class InstanceTest{private volatile static InstanceTest instance;public static InstanceTest getInstance(){   //1if(instance == null){   //2synchronized(InstanceTest.class){   //3if(instance == null){   //4instance = new InstanceTest(); //5}}}return instance;   //6}
}

在并发情况下,如果没有volatile修饰的话,代码中5处会出现问题instance = new InstanceTest();
这个可以分为三步,

a. memory = allocate() //分配内存
b. ctorInstanc(memory) //初始化对象
c. instance = memory   //设置instance指向刚分配的地址

在编译时,指令重排序,不一定按照a->b->c的顺序来执行,可能会是a->c->b,多线程下一个线程在执行完a,马上执行c,设置instance指向刚分配的地址,这个时候另外一个线程刚好到第2步判断,则会出现不为空,直接跳到第6步,而此时并没有初始化,会返回一个未初始化的对象。

七、volatile原理

volatile可以保证线程的可见性并提供一定的有序性,底层采用的是“内存屏障”来实现的,但是不能保证原子性,需要结合lock,synchornized,AtomicInteger 等来实现线程并发安全。
内存屏障会提供三个功能:

  1. 他不会把volatile修饰的变量执行的操作的前面的指令方放到后面,当然相反也不会将后面的指令放到前面。(禁止指令重排序)
  2. 他会强制将当前修改的变量立即写入主缓存
  3. 如果是写操作,他会将其他线程的缓存置为无效

借鉴学习-侵删

这篇关于《volatile使用与学习总结:2023-12-17》多层面分析学习java关键字--volatile的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/509244

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取