Java并发编程实践学习笔记(三)——共享对象之可见性

news/2024/4/28 17:09:00/文章来源:https://blog.csdn.net/ganmaotong/article/details/130584207

目录

1 过期数据        

2 非原子的64位操作

3 锁和可见性

4 Volatile变量(Volatile Variables)


        在单线程环境中,如果向某个变量写入值,在没有其他写入操作的情况下读取这个变量,那么总能得到相同的值。然而,当读写操作在不同的线程中执行时,情况却并非如此。通常,我们无法确保执行读操作的线程能适时的看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。

        下面是一个可见性的例子,在没有同步的情况下共享变量(不要这么做):

public class NoVisibility {private static boolean ready = false;private static int number;private static class ReaderThread extends Thread {public void run() {while (!ready) {// 线程让步, 暂停当前正在执行的线程对象,并执行自己或其他线程。Thread.yield();System.out.println(Thread.currentThread().getName() + ": " + number);}}}public static void main(String[] args) throws InterruptedException {new ReaderThread().start();number = 42;ready = true;System.out.println(Thread.currentThread().getName() + ": " + number);}
}

        运行是没问题的,正常输出42,但不代表这个程序是OK的,在代码中,主线程和读线程都将访问共享变量 ready 和 number。虽然 NoVisibility 看起来会输出 42,但事实上,可能会发生以下两种情况:

(1)Novisibility可能会持续循环,因为ReaderThread可能会看不到写入ready的值。

(2)NoVisibility可能会输出0,因为ReaderThread可能会看到写入ready的值,却没有看到写入number的值。这种现象称为”重排序(Reordering)",在没有同步的情况下,编译器、 处理器、运行时都可能对操作的执行顺序进行调整)..

       只要在某个线程中无法检测到重排序情况,那么就无法确保线程中的操作将按程序中指定的顺序执行。当主线程首先写入 number ,然后再没有同步的情况下写入 ready, 那么读线程看到的顺序可能与写入的顺序完全相反。

        换一个容易复现的例子:

public class VisibilityTest {boolean isStop = false;public void test(){Thread t1 = new Thread(){public void run() {isStop=true;}};Thread t2 =  new Thread(){public void run() {while (!isStop);}};t2.start();t1.start();}public static void main(String args[]) throws InterruptedException {// 为了方便复现,设置了多次循环for (int i = 0; i <30; i++){new VisibilityTest().test();}}
}

        这段代码可能永远不会结束,因为线程t1对isStop的赋值,线程t2可能对此并不可见。解决办法就是把共享变量添加volatile 关键字。这个例子中,这样改动就可以正常退出了:

volatile boolean isStop = false;

1 过期数据        

        Novisibility 展示了缺乏同步可能得到一个已经失效的值:失效数据。当读线程查看ready变量时,可能会得到一个已经失效的值。除非在每次访问变量时都是用同步,否则很可能获得该变量的一个失效值。更糟糕的是,失效值可能不会同时出现:一个线程可能获取到某个变量的最新值,却获得另一个变量的失效值。有时候要确保可见性,仅仅对 set 方法进行同步是不够的,需要对 get 和 set 方法都需要进行同步。

         下面的例子MutableInteger不是线程安全的,因为get和set都是在没有同步的情况下访问value的。失效值问题容易出现:如果某个线程调用了set,那么里一个正在调用get的线程可能会看到更新后的value,也可能看不到。

public class MutableInteger {private int value;public int get() {return value;}public void set(int value) {this.value = value;}
}

       要解决这个问题,需要对set和get用synchronized关键字修饰进行同步。仅对set方法进行同步是不够的,调用get的线程仍然会看到失效值。 

public class SynchronizedInteger {private int value;public synchronized int get() {return value;}public synchronized void set(int value) {this.value = value;}
}

2 非原子的64位操作

        当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值。这种安全性保证也被成为最低安全性(out-of-thin-air satety)

        最低安全性适用于绝大多数变量,但存在例外:非volatile(不稳定的,易变的)类型的64位数值变量(double和long),java内存模型要求,变量的读取曹组和写入操作都必须是原子操作,但对于非volatile类型的long和double变量,JVM允许将64位的读操作或写操作分解为两个32位的操作。当读取一个非volatile的long变量时,如果对该变量的读操作和写操作在不同的线程中执行,那么可能会读取到某个值的高32位和另一个值的低32位。因此,即使不考虑失效数据的问题,在多线程程序中使用共享且可变的long和double等类型的变量也是不安全的,除非用关键字volatile来声明它们,或者用锁保护起来

       虽然 JVM 规范并没有要求64位变量的读写为原子操作,但是现在基本上所有的商业虚拟机都将其实现为原子操作。

3 锁和可见性

        内置锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行结果。如下图,当线程A执行某个同步代码块时,线程B随后进入由同一个锁保护的同步代码块,在这种情况下可以保证,在锁释放之前,A看到的变量值在B获得锁后同样可以由B看到。即当B执行由锁保护的同步代码块时,可以看到A之前在同一个同步代码块中的所有操作。如果没有同步,那么将无法实现上述保证。

       加锁的含义不仅仅局限于同步与互斥,还包括内存可见性。为了保证所有线程都能看到共享变量的最新值,读取和写入线程都必须在同一个锁上进行同步。 

4 Volatile变量(Volatile Variables)

       Java 提供了一种弱同步机制 volatile ,可以用它来保证状态的可见性和有序性。当把变量声明为 volatile 之后,虚拟机在运行当前指令的时候,会建立一个内存屏障(Memory Barrier 或 Memory Fence),阻止重排序时将后面的指令重排序到内存屏障之前的位置。所以,读一个volatile 类型的变量时,总会返回由某一线程所写的最新值。volatile 变量是一种比 synchronized 关键字更轻量级的同步机制。

       虽然 volatile 变量使用十分方便,但也存在着一定的局限性。它通常用来做某个操作完成、发生中断或者状态的标志。 虽然 volatile 变量也可以用于表示其他的状态信息,但使用时要非常小心。例如, volatile 的语义不足以保证递增(count++)操作的原子性。

       下面例子给出了volatile变量的一种典型用法:检查某个状态标记以判断是否退出循环。这个例子中线程通过类似数绵羊的方法进入休眠状态。asleep必须用volatile修饰。否则,当asleep被另一个线程修改时,执行判断的线程却发现不了。这里也可以用锁来确保asleep更新操作的可见性,但这将使代码复杂。

// 数绵羊
volatile boolean asleep;
...while (!asleep)countSomeSheep();

       加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。volatile无法保证对变量的任何操作都是原子性的.

       使用volatile的情况:

     (1)对变量的写入操作不依赖变量的当前值,或确保只有单个线程更新变量的值(如果多个线程都依赖于原值,那么当变量发生非原子操作时,多个线程读取到的变量就不能保证一致了);

     (2)该变量不会与其他状态变量一起纳入不变性条件中;

     (2)在访问变量时不需要加锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_298116.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ALOHA 开源机械臂(Viper 300 Widow X 250 6DOF机械臂组成)第一部分

软件简介&#xff1a; ALOHA 即 A Low-cost Open-source Hardware System for Bimanual Teleoperation&#xff0c;是一个低成本的开源双手遥控操作硬件系统&#xff0c;即开源机械臂。其算法 Action Chunking with Transformers (ACT) 采用了神经网络模型 Transformers&#…

C#学习笔记--实现一个可以重复权重并且能够自动排序的容器--MultiplySortedSet

目录 前言SortedSetC#自带类型自定义类SortedSet权值重复 需求自定义容器 -- MultiplySortedSet核心实现思路 MultiplySortedSet 使用C#自带类型自定义类 前言 最近需要在C#中实现一个功能 有一个容器&#xff0c;该容器能自动对里面的元素进行排序&#xff0c;类似C的优先队列…

FS5175AE降压型1-4节锂电池充电芯片

FS5175AE是一款工作于5V到24V的多串锂电池同步开关降压充电管理芯片。内置MOS管集成了低导通阻抗的NMOS&#xff0c;FS5175AE采用1MHz同步开关架构&#xff0c;实现高 效率充电并简化外围器件&#xff0c;降低BOM成本。通过调节检测电阻&#xff0c;可实现**2A充电电流&#xf…

SpringCloud(22):Sentinel对Feign的支持

Sentinel 适配了 Feign组件。如果想使用&#xff0c;除了引入 spring-cloud-starter-alibaba-sentinel 的依赖外还需要 2个步骤&#xff1a; 配置文件打开 Sentinel 对 Feign 的支持&#xff1a;feign.sentinel.enabledtrue加入 spring-cloud-starter-openfeign 依赖使 Sentin…

基于Linux系统在线安装RabbitMQ

一、前言 二、Erlang下载安装 三、RabbitMQ下载安装 三、RabbitMQ Web界面管理 一、前言 本次安装使用的操作系统是Linux centOS7。 二、Erlang下载安装 在确定了RabbitMQ版本号后&#xff0c;先下载安装Erlang环境。下面演示操作过程&#xff1a; Erlang下载链接&#…

[工具]Pytorch-lightning的使用

Pytorch-lightning的使用 Pytorch-lightning介绍Pytorch-lightning与Pytorch的区别Pytorch-lightning框架的优势Pytorch-lightning框架 重要资源 Pytorch-lightning介绍 这里介绍Pytorch_lighting框架. Pytorch-lightning与Pytorch的区别 Pytorch-lightning可以简单的看作是…

强化学习p3-策略学习

Policy Network (策略网络) 我们无法知道策略函数 π \pi π所以要做函数近似&#xff0c;求一个近似的策略函数 使用策略网络 π ( a ∣ s ; θ ) \pi(a|s;\theta) π(a∣s;θ) 去近似策略函数 π ( a ∣ s ) \pi(a|s) π(a∣s) ∑ a ∈ A π ( a ∣ s ; θ ) 1 \sum_{a\in …

《狂飙》原著来了,邀你重新见证

2023年的开篇十分精彩且丰富&#xff0c;经历过生活的不幸&#xff0c;新的一年万物复兴&#xff0c;每个人心底那颗躁动的心又重新释放&#xff0c;希望新的开始不负所望&#xff0c;年末复盘时所得皆所愿&#xff01; 开篇 开年影视第一炮&#xff0c;炸出了所有人被压抑的内…

告别PPT手残党!这6款AI神器,让你秒变PPT王者!

如果你是一个PPT手残党&#xff0c;每每制作PPT总是让你焦头烂额&#xff0c;那么你一定需要这篇幽默拉风的推广文案&#xff01; 我向你保证&#xff0c;这篇文案将帮助你发现6款AI自动生成PPT的神器&#xff0c;让你告别PPT手残党的身份&#xff0c;成为一名PPT王者。 无论…

计算机图形学 | 实验六:旋转立方体

计算机图形学 | 实验六&#xff1a;旋转立方体 计算机图形学 | 实验六&#xff1a;旋转立方体Z-缓冲GLM函数库PVM矩阵PVM矩阵的使用 华中科技大学《计算机图形学》课程 MOOC地址&#xff1a;计算机图形学&#xff08;HUST&#xff09; 计算机图形学 | 实验六&#xff1a;旋转…

单词词义、词性、例句查询python代码

单词发音、词义、词性、例句查询、输出结果更简洁&#xff0c;一次可查多个单词 运行该代码&#xff0c;命令窗口输入单词&#xff0c;单词用“/”分开&#xff0c;例如&#xff1a;noisy/problem/community/neighbor 可以更多。先安装两个python包requests、 beautifulsoup4&…

卖一辆亏5.8万美元!福特的困扰

随着电动化进入关键的「抢量」周期&#xff0c;加上年初掀起的降价潮&#xff0c;对于还无法适应转型节奏的传统汽车制造商来说&#xff0c;现在是一个艰难的时刻。 本月初&#xff0c;福特首席执行官Jim Farley表示&#xff0c;电动汽车市场的降价是"令人担忧的趋势"…

2023/5/8总结

JAVA基础知识&#xff08;2&#xff09; 1.方法 1、方法定义 格式&#xff1a;public static void 方法名&#xff08;&#xff09;{ //方法体 } 2、方法调用 格式&#xff1a;方法名&#xff08;&#xff09;&#xff1b; 3、方法的通用格式 public static 返回值类型方法名&…

车载测试-can报文解析规则实例

报文解析 报文组成 一般报文主要有以下几个参数&#xff08;比较全的情况下&#xff09; 例 解析报文时主要用到的是帧ID和帧数据 帧ID 接收到的帧ID是十六进制的形式&#xff0c;由29位标识符转换的&#xff0c;目前大多数的通信协议中都直接给出了相应的帧ID&#xff0c…

mathtype不激活能用吗 mathtype产品密钥如何取得

在文档中输入数学式子时一般会用到mathtype&#xff0c;虽然mathtype为广大用户提供了一定期限的试用期&#xff0c;但试用期后如果没有成为正式用户&#xff0c;那么部分功能可能就用不了了。有些小伙伴可能会对mathtype不激活能用吗&#xff0c;mathtype产品密钥如何取得这两…

kt:reified和sam转换(Single Abstract Method Conversions)

什么是refied关键字 ​由于我们都知道Kotlin和Java一样都存在着泛型擦除问题&#xff0c;而Kotlin它知道Java所带来的这个问题&#xff0c;所以对此Kotlin留了一个后门&#xff0c;就是通过inline函数保证使得泛型类的类型实参在运行时能够保留&#xff0c;这样的操作 Kotlin 中…

git简介和使用、基础命令

文章目录 一、git的安装与配置二、Git工作区原理三、Git的使用和仓库的创建四、Git的常用操作五、配置公钥六、IDEA中配置Git 一、git的安装与配置 https://tortoisegit.org/ 下载对应版本安装即可 注意&#xff1a;配置中输入邮箱和密码一定要和自己的git账户一致 git的配置…

copilot 逆向

原文&#xff1a; copilot-explorer | Hacky repo to see what the Copilot extension sends to the server 对我来说&#xff0c;Github Copilot 极其有用。它经常能神奇地读懂我的想法并给出有用的建议。最让我惊讶的是&#xff0c;它能够从周围的代码中正确地“猜测”出函数…

设计模式 -- 备忘录模式

前言 月是一轮明镜,晶莹剔透,代表着一张白纸(啥也不懂) 央是一片海洋,海乃百川,代表着一块海绵(吸纳万物) 泽是一柄利剑,千锤百炼,代表着千百锤炼(输入输出) 月央泽,学习的一种过程,从白纸->吸收各种知识->不断输入输出变成自己的内容 希望大家一起坚持这个过程,也同…

电脑cpu占用率高?怎么办?1分钟快速解决!

案例&#xff1a;电脑cup过高怎么办&#xff1f; 【我的电脑运行缓慢&#xff0c;导致我学习和工作的效率很低。刚刚查看了一下电脑&#xff0c;发现它的cpu占用率很高。有没有小伙伴知道如何解决此电脑cpu过高的问题&#xff1f;】 电脑是我们生活中不可缺少的工具&#xff…