volatile,解决内存可见性引起的问题,wait和notify

补充:synchronized(务必会读(辛可肉耐子)会写),要搭配一个对象的时候,不一定非要是访问的this成员

 

synchronized(锁对象){ 代码块}

public synchronized static void func(){} 静态方法和具体对象无关,和类有关了

相当于synchronized(类.class)

在synchronized()里面没有必要去纠结里main写普通对象还是类对象之类的,synchronized不关心对象是什么,只关心两个线程是否针对同一个对象加锁

static:相当于加上了一个类属性/类方法


一、 💛

内存可见性引起的问题,如下图,你在线程1设定假如他不是等于0就终止,然后你在线程2中给他把值改变了,让他的循环结束,但是我么可以看到,当前我们输入了一个不是0的数字,然后试图改变它,却改变不了,这就是内存可见性的问题

程序在运行的时候,java编译器和jvm可能会对代码进行优化,程序猿们写代码,然后java编译器把你代码改了,保持原有逻辑不变的情况下提高代码效率——编译器优化后

并且优化的效果特别好:服务器的启动步骤非常复杂,启动一个差不多10分钟左右(但是假如我们吧优化关了,可能1个小时打底)

我们想要知道他是如何优化的,就要先清楚while循环的本质,两个指令

1.load读取内存

2.jcmp(比较,并且跳转,寄存器操作,速度极快)

此时编译器发现,代码反复的,快速的读取同一个内存值,并且这个内存值每次读出来的结果还是一样的,此时编译器决定,直接把load优化掉了,只是第一次执行load,后续并不执行load,直接拿寄存器中的数据进行比较了。

但是在另一个线程修改t2线程会不会执行,什么时候去执行,因此产生了误判,导致虽然最后t2的isQuit改动了,但是t1线程中,并未重复load也就会导致出现上述问题了。 

import java.util.Scanner;

public  class  Demo {
    public  static int isQuit=0;                 //静态变量
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {           //创立一个线程
            while(isQuit==0){
                ;
            }
            System.out.println("t1 结束了");

        }); 
        Thread t2 = new Thread(() -> {         //我们去用t2来改变t1的值
            Scanner scanner=new Scanner(System.in);
            System.out.println("请输入isQuit的数字");
            isQuit=scanner.nextInt();    
        });
        t1.start();
        t2.start();


    }
}

volatile(会读会写 (wao(平🐍)里太哦)弥补上述缺口:意思易变的,修饰一个变量之后,编译器就明白,这个变量是一边倒,就不能按照上述方式处理代码(把读操作优化到寄存器中),让编译器禁止优化,于是保证t1在循环的过程中,始终都能读取内存中的数据

volatile本质是保持变量的内存可见性

见下面代码用法:

下面得到的结果就是正确的,我就暂时省略结果了。

import java.util.Scanner;

public  class  Demo {
    public volatile static int isQuit=0;          //变量static前面
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            while(isQuit==0){
                ;
            }
            System.out.println("t1 结束了");

        });
        Thread t2 = new Thread(() -> {
            Scanner scanner=new Scanner(System.in);
            System.out.println("请输入isQuit的数字");
            isQuit=scanner.nextInt();
        });
        t1.start();
        t2.start();


    }
}

编译器的优化是一个“玄学问题”,就比如说,我在新代码里面,写了个sleep,同时把volatile取消了,但是这样也正确

可以理解为,加上sleep之后,sleep就会大幅度的影响到while循环的速度,速度慢了,编译器也不打算继续优化了~此时即使不加volatile,也能够及时感知到内存变化了,sleep到底间隔多久,会触发优化,只有那些码届远古大能才知道。 

二、💜 

另一种解释方式

Java的内存模型(JMM)

其实这个理解和上面那个编译器解释原理是一样的,从主内存读,但反复读都是一样的,所以直接就去工作内存读,但是工作内存又是什么鸟东西捏?

工作内存:不是我们平时说的内存,而是cpu的寄存器和cpu缓存统称为工作内存,有人可能会好奇那为啥不叫cpu寄存器+cpu缓存呢(猜:JAVA宣称是跨平台,但是cpu的话又是有一部分硬件知识,他们希望java程序猿可以不用掌握太多的硬件知识。

 内存可见性和加锁描述了线程安全问题的典型情况和处理方式。


三、 💙 

wait(等待)和notify(通知):用来协调线程顺序的重要工具,多线程调度是随机的~很多时候希望多个线程按照咱们规定的顺序来执行,完成线程之间的配合工作。

上面两个都是object提供的方法,也就是说任意对象,当wait引起线程阻塞之后,可以用interrupt方法,把线程给唤醒,打断当前线程的阻塞状态的。

wait执行程序的时候会干三件事:

1.解锁

2.阻塞等待

3.当被其他线程唤醒之后,就会尝试重新加锁,加锁成功,wait执行完毕,继续往下执行其他逻辑。

也就是说wait(需要先加锁)

核心思路:先加锁,在synchronized里面进行wait(加锁要加同一个对象上面),这里的线程会一直阻塞到其他线程notify。

其中最典型的场景就是有效的避免线程饥饿/线程饿汉

 

几个注意

1.要想notify能顺利唤醒wait,就需要确保wait和notify都是同一个对象调用的,

2.wait和notify都需要放到synchronzied之中的,虽然notify不涉及到解锁操作

3.如果进行notify的时候,另一个线程没有处于wait状态,此时notify也没有任何副作用。

t2可以理解成辅助t1的线程,使用notify线程对其他线程统筹安排作用。

import java.util.Scanner;

public  class  Demo {
    public  static Object locker=new Object();
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            while(true){
               synchronized (locker) {
                   System.out.println("t1 开启");       //第一步执行的
                   try {
                       locker.wait();                  //第二步执行,t2陷入阻塞状态
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println("t1 over");      //第五步,t1被唤醒
               }
               }
        });
        Thread t2 = new Thread(() -> {
         while (true) {
             try {
                 Thread.sleep(100);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             synchronized (locker) {
                 System.out.println("t2 开启");      //第三步执行的
                 locker.notify();                   //第四部执行,唤醒t1
                 System.out.println("t2 over");
             }
         }
        });
        t1.start();
        t2.start();


    }
}

 

线程可能有多个~

几个线程wait,一个线程复制notify,notify只会唤醒一个线程,具体哪个随机,notifyAll会唤醒全部线程(但是不推荐去用),这种也是全随机,就和wait的初心违背了。

如果要唤醒某个特定的线程,就要让不同的线程,使用不同的对象来进行wait,想要唤醒谁,就可以使用对应的对象notify

wait和sleep的区别

sleep有明确的使用是假,到达时间自动被唤醒,也能提前用interrupt

wait:死等,一直等到其他线程notify(但是,是正常唤醒,可继续工作,还会进入wait 状态),wait也可以被interrupt提前唤醒(但是这个是来通知线程要结束了,线程要收尾了)

,当然他也有带时间版的和join差不多,因此协调多个线程执行顺序wait比notify更牛一些

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

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

相关文章

木马免杀(篇一)基础知识学习

木马免杀(篇一)基础知识学习 ———— 简单的木马就是一个 exe 文件,比如今年hw流传的一张图:某可疑 exe 文件正在加载。当然木马还可能伪造成各式各样的文件,dll动态链接库文件、lnk快捷方式文件等,也可能…

.net core的Knife4jUI,让swagger更精致

要在 .NET Core 中使用 IGeekFan.AspNetCore.Knife4jUI,您可以按照以下步骤进行配置: 首先,安装 IGeekFan.AspNetCore.Knife4jUI NuGet 包。可以通过 Visual Studio 的 NuGet 包管理器或者 .NET CLI 进行安装。 在 Startup.cs 文件的 Config…

电脑数据怎么加密?电脑数据加密软件有哪些?

在生活和工作中,我们总离不开电脑,而电脑中那些重要的数据,需要我们加密保护。那么电脑数据该怎么加密呢?电脑数据加密软件又有哪些呢?下面我们就来了解一下吧。 电脑数据加密软件 一般来说,常见的电脑加密…

【从零学习python 】10.Python条件语句和if嵌套详解

文章目录 elif一、 elif的功能二、注意点if嵌套想一想: 一、if嵌套的格式二、if嵌套的应用为什么结果3和结果4相同??? 猜拳游戏运行效果:参考代码: if补充内容三、自动类型转换进阶案例 elif 如果有这样一种情况:当条件…

Electron学习1 安装环境与第一个程序

Electron学习1 安装环境与第一个程序 一、 Electron 简介二、安装 nvm三、安装nodejs四、安装nrm五、安装electron1. npm 初始化2. 创建 package.json3. 安装electron4. 创建一个页面5. 创建文件main.js6. 创建预加载器文件 preload.js7. 启动程序 六、打包 一、 Electron 简介…

TS学习笔记

一、变量与常量 //1、类型 2、面向对象 //输出语句、 //变量 //声明变量 字母数字下划线 驼峰命名 let personname "李逍遥001"; var personname1 "李逍遥002"; personname "李逍遥003" document.write(personname " " pe…

希尔排序——C语言andPython

前言 步骤 代码 C语言 Python 总结 前言 希尔排序(Shell Sort)是一种改进的插入排序算法,它通过将数组分成多个子序列进行排序,逐步减小子序列的长度,最终完成整个数组的排序。希尔排序的核心思想是通过排序较远距…

【ChatGPT 指令大全】销售怎么借力ChatGPT提高效率

目录 销售演说 电话销售 产出潜在客户清单 销售领域计划 销售培训计划 总结 随着人工智能技术的不断进步,我们现在有机会利用ChatGPT这样的智能助手来改进我们的销售工作。在接下来的时间里,我将为大家介绍如何运用ChatGPT提高销售效率并取得更好的…

苹果电脑图像元数据编辑器:MetaImage for Mac

MetaImage for Mac是一款功能强大的照片元数据编辑器,它可以帮助用户编辑并管理照片的元数据信息,包括基本信息和扩展信息。用户可以根据需要进行批量处理,方便快捷地管理大量照片。 MetaImage for Mac还提供了多种导入和导出格式&#xff0…

多用户跨境电商商品库系统快速搭建(全开源)

搭建一个多用户跨境电商商品库系统需要以下步骤: 1. 确定系统需求:首先,需要明确系统的功能需求,包括商品管理、订单管理、用户管理、支付管理等。根据具体需求确定系统的功能和界面设计。 2. 确定技术栈:选择合适的…

超融合基础架构 (HCI) 监控

什么是超融合基础架构 (HCI) 超融合基础架构 (HCI) 是一种软件定义的基础架构技术,它将计算、虚拟化和网络功能全部整合到一个设备中。超融合基础架构 (HCI) 解决方案使用软件和 x86 服务器来取…

【多维定向滤波器组和表面波】表面变换:用于高效表示多维 s 的多分辨率变换(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

【校招VIP】java语言考点之static和并发

考点介绍: static考点是面试的高频考点,很多同学不理解使用场景,只是从加载出发。 一般从容易到难提问,比如从static的含义和理解、到JVM的存储或者到线程安全性,再到单例模式等。 java语言考点之static和并发 相关题…

本质矩阵E、基本矩阵F、单应矩阵H

1. E (归一化坐标对进行计算) t ^ R 为3*3的矩阵, 因为R,t共有6个自由度,又因为单目尺度等价性,所以实际上E矩阵共有5个自由度。因此至少需要5个点对来求解。 2. 基本矩阵F:根据两帧间匹配的像素点对儿计算 3*3且自由度为7的矩阵kF也为基础矩阵&#x…

c语言——三子棋

基本框架 三个文件: 其中.cpp文件用于游戏具体函数设计&#xff0c;.h文件为游戏的函数声明&#xff0c;test.cpp文件用于测试游戏运行。 需要用到的头文件&#xff1a; #include <stdio.h> #include <stdlib.h>//rand&srand #include <time.h>//时间相…

【BASH】回顾与知识点梳理(十四)

【BASH】回顾与知识点梳理 十四 十四. 文件与目录的默认权限与隐藏权限14.1 文件预设权限&#xff1a;umaskumask 的利用与重要性&#xff1a;专题制作 14.2 文件隐藏属性chattr (配置文件案隐藏属性)lsattr (显示文件隐藏属性) 14.3 文件特殊权限&#xff1a; SUID, SGID, SBI…

《剑指offer》(6)其他算法

先计算下三角&#xff0c;再遍历一次计算上三角。 class Solution: def multiply(self , A: List[int]) -> List[int]: #长度判断 n len(A) if n < 1: return [] B [1]*n #计算乘矩阵的下三角&#xff0c;B中的每一个数都是A的前一个数和B的前一个数相乘 for i in ran…

Linux系统中的自旋锁(两幅图清晰说明)

总结&#xff1a; 多CPU下的自旋锁采取的是忙等待&#xff08;原地打转&#xff09;机制&#xff0c;虽然忙等待的线程占用了它所在的cpu&#xff0c;但其他线程仍可放到其他CPU上执行。所以自旋锁上锁和解锁之间的临界区代码要尽量的短&#xff0c;最好不要超过5行&#xff0c…

【Linux拓展】ncurses库的安装和使用 {ncurses库的安装方法,ncurses库的使用手册,基于终端的贪吃蛇游戏}

一、简介 ncurses库是一个用于创建基于终端的交互式应用程序的库。它提供了一套API&#xff0c;用于处理终端界面的输入和输出&#xff0c;以及控制终端的光标位置、颜色、窗口等。 使用ncurses库&#xff0c;您可以在终端中创建复杂的文本界面&#xff0c;包括窗口、菜单、按…

能化校对软件:提高招标文件质量的创新解决方案

智能化校对软件是一种创新的解决方案&#xff0c;可以进一步提高招标文件的质量和准确性。 以下是一些智能化校对软件的创新功能和优势&#xff1a; 1.自然语言处理(NLP)技术&#xff1a;智能化校对软件利用NLP技术来理解和分析文本&#xff0c;识别和纠正更复杂的语法和语义错…