共享模型之内存(一)

news2024/12/25 13:05:19

1.Java内存模型

1>.JMM即Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等;

2>.JMM体现在以下几个方面:

①.原子性 - 保证指令不会受到线程上下文切换的影响;
②.可见性 - 保证指令不会受cpu缓存的影响;
③.有序性 - 保证指令不会受cpu指令并行优化的影响;

2.可见性

2.1.问题: 退不出的循环

1>.main线程对run变量的修改对于t线程不可见,导致了t线程无法停止

@Slf4j
public class TestThreadDemo1 {
    // 共享变量
    static boolean run = true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            while(run){
                // ....
            }
        },"t1").start();

        Thread.sleep(1000);

        log.info("程序运行结束");

        run = false; // 线程t不会如预想的停下来
    }
}

在这里插入图片描述
分析:

①.初始状态:t线程刚开始从主内存读取了变量"run"的值到工作内存;
在这里插入图片描述
②.因为t线程要频繁从主内存中读取变量"run"的值,JIT编译器会将变量"run"的值缓存至(t线程)自己工作内存的高速缓存中,减少对主存中变量"run"的访问,提高效率;
在这里插入图片描述
③.1秒之后,main线程修改了变量"run"的值,并同步至主存,但是t线程还是从自己工作内存的高速缓存中读取这个变量的值,结果永远是旧值;
在这里插入图片描述

2.2.解决方案: volatile(易变关键字)

1>.volatile关键字可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找/获取变量的值,要求线程必须到主存中获取它的值,线程操作volatile变量都是直接操作主存中对应的变量;

@Slf4j
public class TestThreadDemo1 {
    // 共享变量
    volatile static boolean run = true;

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            while(run){
                // ....
            }
        },"t1").start();

        Thread.sleep(1000);

        log.info("程序运行结束");

        run = false; // 线程t不会如预想的停下来
    }
}

在这里插入图片描述
###扩展: Synchronized也可以保证变量的可见性

@Slf4j
public class TestThreadDemo1 {
    // 共享变量
    static boolean run = true;

    //对象锁
    final static Object OBJ = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                synchronized (OBJ){
                    if (!run){
                        break;
                    }
                }
            }
        }, "t1");
        t1.start();

        Thread.sleep(1000);
        log.info("程序运行结束");
        synchronized (OBJ){
            run = false;
        }
    }
}

在这里插入图片描述

2.3.可见性VS原子性

1>.前面例子体现的实际就是可见性,它保证的是在多个线程之间,一个线程对volatile变量的修改对另一个线程是可见的,但是volatile不能保证原子性,仅适用在一个写线程,多个读线程的情况;

上例从字节码理解是这样的:
在这里插入图片描述
2>.比较一下之前我们将线程安全时举的例子:两个线程一个i++一个 i-- ,只能保证看到最新值,不能解决指令交错;
在这里插入图片描述
***注意:

①.synchronized语句块既可以保证代码块的原子性,也同时保证代码块内变量的可见性.但缺点是synchronized是属于重量级操作,性能相对更低;

②.如果在前面示例的死循环中加入"System.out.println()"语句会发现即使不加volatile修饰符,线程t也能正确看到其他线程对"run"变量的修改了,想一想为什么?
在这里插入图片描述

2.4.设计模式–两阶段终止模式

1>.在一个线程T1中如何"优雅"终止线程T2? 这里的"优雅"指的是给T2 一个料理后事的机会;

2.4.1.错误思路

1>.使用线程对象的stop()方法停止线程;

stop方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁;

2>.使用System.exit(int)方法停止线程

目的仅是停止一个线程,但这种做法会让整个程序都停止;

2.4.2.解决方案: 利用停止标记

在这里插入图片描述

@Slf4j
public class TestTwoPhaseTermination {

    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination twoPhaseTermination = new TwoPhaseTermination();
        twoPhaseTermination.start();

        Thread.sleep(3500);
        log.info("停止监控");
        twoPhaseTermination.stop();
    }
}

@Slf4j
class TwoPhaseTermination {
    //监控线程
    private Thread monitorThread;

    //设置一个线程停止的标记,使用volatile是为了保证该变量在多个线程之间的可见性
    private volatile boolean stop = false;

    //启动监控线程
    public void start() {
        monitorThread = new Thread(() -> {
            //循环不停执行
            while (true) {
                //当前线程是否可以停止
                if (stop) {
                    //如果当前线程被打断,可以退出循环
                    log.info("料理后事");
                    break;
                }

                //如果线程没有停止,执行监控操作
                try {
                    Thread.sleep(1000);
                    log.info("执行监控记录");
                } catch (InterruptedException e) {
                }

                //执行监控操作
            }
        }, "监控线程");
        monitorThread.start();
    }

    //停止监控线程
    public void stop() {
        //通过修改volatile变量的值来停止监控线程
        stop = true;
        //为了防止监控线程在阻塞过程中线程停止的标记被修改了导致监控线程需要阻塞很长时间之后才能停止
        //这里还可以再加一段线程打断的代码立刻打断处于阻塞状态的监控线程马上结束运行!!
        monitorThread.interrupt();
    }
}

在这里插入图片描述

2.5.设计模式(同步模式)-- Balking(犹豫)模式

1>.犹豫模式用在一个线程发现另一个线程或本线程已经做了某一件相同的事,那么本线程就无需(重复)再做了,直接结束返回;

2.5.1.实现

@Slf4j
public class TestBalking {

    public static void main(String[] args) throws InterruptedException {
        Balking balking = new Balking();
        balking.start();

        Thread.sleep(3500);
        log.info("停止监控");
        balking.stop();
    }
}

@Slf4j
class Balking {
    //监控线程
    private Thread monitorThread;

    //设置一个线程停止的标记
    private volatile boolean stop = false;

    //标记某个方法是否已经被执行过
    private boolean starting = false;

    //启动监控线程
    public void start() {
        synchronized (this){
            //监控线程只需要被执行一次即可,无需重复执行
            if (starting) {
                return;
            }

            //当监控线程已经执行过了,标记一下
            starting = true;
        }

        monitorThread = new Thread(() -> {
            //循环不停执行
            while (true) {
                //当前线程是否可以停止
                if (stop) {
                    //如果当前线程被打断,可以退出循环
                    log.info("料理后事");
                    break;
                }

                //如果线程没有停止,执行监控操作
                try {
                    Thread.sleep(1000);
                    log.info("执行监控记录");
                } catch (InterruptedException e) {

                }
            }
        }, "监控线程");

        monitorThread.start();
    }

    //停止监控线程
    public void stop() {
        //通过修改volatile变量的值来停止监控线程
        stop = true;
        //为了防止监控线程在阻塞过程中线程停止的标记被修改了导致监控线程需要阻塞很长时间之后才能停止
        //这里还可以再加一段线程打断的代码立刻打断处于阻塞状态的监控线程马上结束运行!!
        monitorThread.interrupt();
    }
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大型会场活动线上保障方案

背景 为保证活动上线后的质量,大型会场活动上线前通常会预设一些线上可能出现的问题,提前制定保障方案。 这些与活动保障相关的问题可能与App端上的容器环境有关,也可能与大盘用户设备特征有关,问题的处理方案会影响活动的线上效…

《啊哈算法图的遍历》(14张图解)

目录 前言 一,dfs和bfs是什么 二,城市地图--图的深度优先遍历 三,最少转机--图的广度优先遍历 前言 🌼说爱你(超甜女声版) - 逗仔 - 单曲 - 网易云音乐 1月22日一个女孩加了我,她和我聊音…

adb常用指令合集

adb文件管理指令 1.复制设备里的文件到电脑 adb pull <设备里的文件路径> [电脑上的目录] 电脑上的目录 参数可以省略&#xff0c;默认复制到当前目录 例&#xff1a;adb pull /data/tsplogtool /home/jxq/文档/场景魔方 2.复制电脑里的文件到设备 adb push <电脑上的…

浅谈未来10年IT行业的变局与抉择,一文带你认识元宇宙

一. 困局据国家就业部门最新统计数据报告&#xff0c;2022年应届毕业生的数量首次突破1000万大关。其中研究生达到130万&#xff0c;985、211等名校毕业生75万&#xff0c;普通本科毕业生470万&#xff0c;专科生460万&#xff0c;另外还有几十万的归国留学生&#xff01;但这还…

《从0开始学大数据》之Spark性能优化案例

基于软件性能优化原则和 Spark 的特点&#xff0c;Spark 性能优化可以分解为下面几步。 性能测试&#xff0c;观察 Spark 性能特性和资源&#xff08;CPU、Memory、Disk、Net&#xff09;利用情况。分析、寻找资源瓶颈。分析系统架构、代码&#xff0c;发现资源利用关键所在&a…

【前端】Vue项目:旅游App-(17)home:页面滚动显示搜索栏、节流、时间同步

文章目录目标过程与代码页面滚动到目标位置显示搜索框优化&#xff1a;节流搜索栏显示时间同步效果总代码修改或添加的文件search-bar.vueuseScroll.jsstore的main.jsformatDate.jshome.vue参考本项目博客总结&#xff1a;【前端】Vue项目&#xff1a;旅游App-博客总结 目标 …

HDFS文件浏览器功能OOM排查

现象描述 涉及HDFS文件浏览器的某个功能运行一段时间后会出现OOM的情况 错误日志如下&#xff1a; service.log.2023-02-01-0.log:java.lang.OutOfMemoryError: Java heap space排查过程 需要查看dump文件排查一下造成OOM的原因 查看jvm参数如下&#xff1a; java -Duser.t…

一文讲明Docker的基本使用,常见Docker命令使用 、Docker的安装使用等【详细说明+图解+概念+实践】

一个混迹于Github、Stack Overflow、开源中国、CSDN、博客园、稀土掘金、51CTO等 的野生程序员。 目标&#xff1a;分享更多的知识&#xff0c;充实自己&#xff0c;帮助他人 GitHub公共仓库&#xff1a;https://github.com/zhengyuzh 以github为主&#xff1a; 1、分享前端后端…

【Python合集系列】2023兔年吉祥,新的一年希望放烟花的人跟看烟花的人都能平平安安哦~(附多种源码)

前言 希望放烟花的人跟看烟花的人都能平平安安。 &#x1f440; NICE TO MEET YOU :)&#x1f319; 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 ​哈喽&#xff01;我是木子&#xff0c;新…

设计模式之适配器模式,以C++为例。

今天来盘一盘适配器模式。适配器&#xff1a;顾名思义&#xff0c;就是让原本不合适的变为合适的&#xff0c;好似一对男女&#xff0c;没有中间的媒婆是不会互相了解的&#xff0c;好像不太恰当&#xff0c;就这么解释吧&#xff0c;只有有了这个中间人他们才会产生联系&#…

智能驾驶开启高精定位新赛道,这家供应商正加码布局海外市场

高工智能汽车研究院监测数据显示&#xff0c;2022年1-11月中国市场乘用车前装标配搭载NOA交付达到18.38万辆&#xff0c;同比增长91.86%&#xff1b;同时&#xff0c;NOA搭载的车型配置价格还在不断下滑&#xff0c;正在把NOA的配置拉至15万元价格区间。 而作为高精定位&#x…

面向对象——static(静态)Math类自定义工具类代码块

目录 static&#xff08;静态&#xff09;关键字 static的注意事项 static的优点和缺点 应用场景 自定义工具类 代码块 static&#xff08;静态&#xff09;关键字 static是一个修饰符&#xff0c;用于修饰成员&#xff08;成员变量 、成员方法&#xff09;static的特点…

Redis处理client连接数过多,大量空闲链接无法释放问题

打开redis命令终端&#xff0c;输入&#xff1a; client list 查看连接数&#xff0c;用于返回所有连接到服务器的客户端信息和统计数据 参数解析&#xff1a; id: 唯一的64位的客户端ID(Redis 2.8.12加入)。 addr: 客户端的地址和端口 fd: 套接字所使用的文件描述符 age…

python真的很骚可惜你不会

python基本语法 &#x1f4d2;博客主页&#xff1a; 微笑的段嘉许博客主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐留言&#x1f4dd; &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &#x1f4c6;51CTO首发时间&#xff1a;&#x1f334;2023年1月日3…

redis分布式缓存

文章目录一、redis持久化1.1.RDB持久化1.1.1.执行时机1.1.2.RDB原理1.1.3.小结1.2.AOF持久化1.2.1.AOF原理1.2.2.AOF配置1.2.3.AOF文件重写1.2.4.小结1.3.RDB与AOF对比二、Redis主从集群2.1.集群结构2.2.准备实例和配置2.3.启动2.4.开启主从关系2.5.测试2.6.主从数据同步原理2.…

Codeforces Round #848 (Div. 2) A-E 赛时思路+正解

青大蒟蒻第一次在正式的div2div2div2中AcAcAc了五道题&#xff0c;也是小蒟蒻有史以来发挥最好的一场&#xff0c;这场过后我的cf也许可能也要变成黄了。 A. Flip Flop Sum 题意&#xff1a;给定一个a——ia——ia——i数组&#xff0c;权值仅为1或-1&#xff0c;我们选择相邻…

《死亡空间》重制回归!无法启动怎么办?

作为科幻生存恐怖系列的经典之作&#xff0c;《死亡空间》在推出15年后再次回归&#xff0c;果然引发热潮。精美震撼的科幻场景&#xff0c;强烈的视觉画面&#xff0c;加上阴森的3D 音效&#xff0c;重制版提升了身临其境之感&#xff0c;完全是沉浸式恐怖体验&#xff0c;只能…

红外遥控数码管显示

红外遥控器实物图红外遥控器接口电路数码管接口电路红外遥控数码管显示程序源代码/**************************红外遥控数码管显示************************** * 单片机&#xff1a;51单片机* 开发环境&#xff1a;keil * 名称:红外遥控数码管显示 * 功能&#xff1a;遥控器红外…

C语言常量

常量是固定值&#xff0c;在程序执行期间不会改变。这些固定的值&#xff0c;又叫做字面量。常量可以是任何的基本数据类型&#xff0c;比如整数常量、浮点常量、字符常量&#xff0c;或字符串字面值&#xff0c;也有枚举常量。常量就像是常规的变量&#xff0c;只不过常量的值…

OpenMMLAB AI实战营第一课笔记

计算机视觉的发展 计算机视觉是什么 计算机视觉是一门让计算机学会"看"的学科&#xff0c;研究如何自动理解图像和视频中的内容 计算机视觉的发展 早期萌芽&#xff08;1960-1980&#xff09; 统计机器学习与模式识别(1990-2000) ImageNet 大型数据库(2006) 斯坦…