JavaEE初阶-线程3

news2024/11/28 6:34:07

文章目录

  • 一、线程安全问题-内存可见性
  • 二、等待通知
    • 2.1 wait()方法
    • 2.2 notify()方法


一、线程安全问题-内存可见性

import java.util.Scanner;

public class Demo27 {
    private static int count=0;
    //下面这段代码会出现内存的可见性问题
    //将从内存中读取count值的操作称为load 判断操作称为cmp
    //load和cmp的执行速度差了好几个数量级,在线程2开始执行代码提示输入数字时,线程1的while循环已经执行了很多遍
    //java编译器会自动给代码进行优化
    //导致load只是第一次时真正从内存中读取count值,其余都是从cpu的寄存器中读取
    //然而线程2修改count是在内存中进行修改,线程1根本访问不到count的值
    //可以在变量前加上volatile关键字来提醒编译器不要优化
    public static void main(String[] args) {

        Thread t1=new Thread(()->{

            while(count==0) {
                //
            }
            System.out.println("t1 执行结束");
        });

        Thread t2=new Thread(()->{
            Scanner scanner=new Scanner(System.in);
            System.out.println("输入数字:");

            count=scanner.nextInt();
        });


        t1.start();
        t2.start();

    }

}

以上代码建立一个线程t1和线程t2,线程t1中建立一个循环,线程t2控制循环中的条件,按照预期,应该在t2中改变条件之后t1结束,整个程序也应该结束,但事实并非如此。
在这里插入图片描述
输入数字之后线程一仍然没有结束,这是因为count==0这个条件涉及到两个操作load和cmp,先将count的值从内存中载入cpu的寄存器进行比较,在线程二中还未输入数字时,线程一的循环已经执行很多次了,编译器发现这么多次count的值没有变化就会进行优化,只有第一个load会从内存中读count的值,剩余次数都是直接用寄存器中的值,这样既使后来在线程二当中改变了内存中的count的值,因为循环条件的count不从内存中读值因此访问不到修改后的count,线程一就会继续循环,从而程序就结束不了。如果不想让编译器进行优化,可以在count前面加上volatile关键字即可。
另外上述的内存可见性问题加锁,即包含在synchronized内也可以解决,因为加锁是比较重量的,load相对就不算慢了就不会触发优化。在循环内加上sout输出语句也是一个道理,sout相对于load更慢无法触发优化,自然也能解决上述的内存可见性的问题。

在网上查询资料还可以看到以一种JMM(java内存模型)角度来理解该问题的解答:
当t1执行的时候,会从工作内存中读取count而不是从主内存中。
t2修改count会先修改工作内存,再同步到主内存,但由于t1没有重新读主内存,导致t1没有感知到t2的修改。

二、等待通知

2.1 wait()方法

代码示例如下:

public class Demo28 {

    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
        
        System.out.println("等待之前");
        //wait必须在synchronized内使用,会解锁并且让线程进入阻塞等待状态
        synchronized (locker) {
            locker.wait();
        }

        System.out.println("等待之后");

    }
}

wait方法是object类的方法,任何类对象都可以使用,它只能在锁内使用,如果执行wait方法会释放锁并且线程进入阻塞状态waiting。wait也有限时的参数,加上时间,会在限定时间内阻塞之后自动唤醒。
注意:wait和sleep一样可以被interrupt打断并且清空标志位。

2.2 notify()方法

通过notify方法来唤醒wait方法进入阻塞的线程,wait的线程状态从Waiting->Runnable->Blocked。
代码示例如下:

public class Demo29 {
    public static void main(String[] args) throws InterruptedException {

        Object locker = new Object();
        //1.wait和notify必须都在synchronized内
        //2.一个notify唤醒一个wait() 如果多个线程wait就使用多个notify或者notifyAll 这种唤醒时随机的
        //3.如果想唤醒指定线程也可以必须一对一写好锁对象
        //4.notify需要在wait之后

        Thread t1 = new Thread(() -> {
            System.out.println("t1 等待之前");
            synchronized (locker) {
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t1 等待之后");


        });


        Thread t2 = new Thread(() -> {
            System.out.println("t2 通知之前");
            Scanner scanner = new Scanner(System.in);

            synchronized (locker) {
//控制阻塞,不输入数字t1仍是阻塞
                scanner.nextInt();
                locker.notify();
            }
            System.out.println("t2 通知之后");
        });


        t1.start();
        t2.start();

    }

}

notify必须也在synchronized之内,并且一个notify只能唤醒对应的一个wait,如果多个线程wait就使用多个notify或者notifyAll,这种唤醒时随机的,唤醒的前提是通过一个对象来调用的wait及notify方法。如果想唤醒指定线程也可以必须一对一写好锁对象。另外notify的使用必须在wait之后,如果在wait之前使用notify不会有任何效果,不过wait就没有办法唤醒了。
使用相应的锁对象进行一对一唤醒代码示例如下:

public class Demo30 {

    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
        Object locker1 = new Object();
        Thread t1 = new Thread(() -> {

            synchronized (locker) {
                System.out.println("t1等待之前");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("t1等待之后");

            }

        });

        Thread t2 = new Thread(() -> {
            synchronized (locker1) {
                System.out.println("t2等待之前");
                try {
                    locker1.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("t2等待之后");
            }


        });


        Thread t3 = new Thread(() -> {
            synchronized (locker) {
                //locker.notifyAll();
                locker.notify();
            }

            synchronized (locker1) {
                locker1.notify();
            }


        });


        t1.start();
        t2.start();

        Thread.sleep(1000);
        t3.start();

    }

}

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

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

相关文章

LINUX笔记温习

目录 DAY1 DAY2 day3: day4 day5 day6 day7 day8 day9 day10 day11 day12 day13 day14 day15 20day DAY1 1、多层级文件夹创建要带-p; 2、创建多文件,要先到该目录下才能创建(第一个目录必须存在才能有效建立); D…

简单说清楚什么是SQL Injection?

最近看完了《The Pragmatic Programmer: 20th Anniversary Edition, 2nd Edition: Your Journey to Mastery》,在第7章:While You Are Coding的footnotes中,提到了一幅漫画: 这不仅用简单的方式说清楚了什么是SQL Injection&#…

Veritas NetBackup 10.4 (Unix, Linux, Windows) - 一流的企业备份解决方案

Veritas NetBackup 10.4 (Unix, Linux, Windows) - 一流的企业备份解决方案 The #1 enterprise backup and recovery solution. 请访问原文链接:https://sysin.org/blog/veritas-netbackup-10/,查看最新版。原创作品,转载请保留出处。 作者…

【THM】Active Reconnaissance(主动侦察)-初级渗透测试

介绍 在网络安全模块的第一个房间里,我们主要进行被动侦察。在第二个房间中,我们重点关注主动侦察以及与之相关的基本工具。我们学习使用网络浏览器来收集有关我们目标的更多信息。此外,我们讨论使用简单的工具(例如ping、traceroute、telnet和 )nc来收集有关网络、系统和…

陀螺仪传感器,IMU和加速度计的产品和选型

爱普生陀螺仪传感器是一种角速度传感器,作为一种石英电子式陀螺仪芯片,具有温度特性好、功耗低、成本低、稳定性好等特点。目前EPSON主力单轴陀螺仪传感器型号为XV7001BB、XV7011BB、XV7021BB和XV7181BB。针对扫地机器人传感器模组等领域的需要&#xff…

【详细教程制作】用户列表

👨‍💻个人主页:开发者-曼亿点 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 曼亿点 原创 👨‍💻 收录于专栏&#xff1a…

正则表达式引擎库汇合

1.总览表格 一些正则表达式库的对比 index库名编程语言说明代码示例编译指令1Posix正则C语言是C标准库中用于编译POSIX风格的正则表达式库 posix-re.cgcc posix-re.c 2PCRE库C语言提供类似Perl语言的一个正则表达式引擎库。 一般系统上对应/usr/lib64/libpcre.so这个库文件&am…

012——LED模块驱动开发(基于I.MX6uLL)

目录 一、 硬件原理图 二、 驱动程序 三、 应用程序 四、 Makefile 五、操作 一、 硬件原理图 又是非常经典的点灯环节 ,每次学新语言第一步都是hello world,拿到新板子或者学习新的操作系统,第一步就是点灯。 LED 的驱动方式&#xff0…

bugku-web-聪明的php

传递一个参数&#xff0c;提示flag的文件名随意 这里就是要传递参数filename&#xff0c;其值随意 直接得到内部逻辑代码 <?php include(./libs/Smarty.class.php); echo "pass a parameter and maybe the flag files filename is random :>"; $smarty new S…

蓝队面经速查手册

文章目录 常见安全设备溯源应急响应&#xff0b;入侵排查思路日志文件安全加固常用排查命令主机后门webshell的排查思路Webshell工具的流量特征Linux 的 Selinuxwindows 日志分析工具Linux 日志分析技巧命令安全基线规范检查中间件基线规范&#xff08;APACHE&#xff09;中间件…

验证码项目(java实现)

1、Kaptcha详细配置 配置项 配置说明 默认值 kaptcha.border 图⽚边框&#xff0c;合法值&#xff1a;yes , no yes kaptcha.border.color 边框颜⾊&#xff0c;合法值&#xff1a; r,g,b (and optional alpha) 或者 white,black,blue black kaptcha.image.width 图⽚宽 200…

golang语言系列:Scrum、Kanban等敏捷管理策略

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 golang语言系列 文章&#xff0c;主要对编程通用技能 Scrum、Kanban等敏捷管理策略 进行学习 1.什么是敏捷开发 敏捷是一个描述软件开发方法的术语&#xff0c;它强调增量交付、团队协作、持续规划和持续学习。…

ZKFair 创新之旅,新阶段如何塑造财富前景

在当前区块链技术的发展中&#xff0c;Layer 2&#xff08;L2&#xff09;解决方案已成为提高区块链扩容性、降低交易成本和提升交易速度的关键技术&#xff0c;但它仍面临一些关键问题和挑战&#xff0c;例如用户体验的改进、跨链互操作性、安全性以及去中心化程度。在这些背景…

基于springboot+vue+Mysql的某银行OA系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

JimuReport积木报表 v1.7.4 正式版本发布,免费的JAVA报表工具

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

Php_Code_challenge16

题目&#xff1a; 答案&#xff1a; 解析&#xff1a; 所以科学计数法绕过即可。

AI预测福彩3D第23弹【2024年4月1日预测--第5套算法开始计算第5次测试】

今天&#xff0c;咱们继续进行本套算法的测试&#xff0c;本套算法目前也已命中多次。今天为第五次测试&#xff0c;仍旧是采用冷温热趋势结合AI模型进行预测。好了&#xff0c;废话不多说了。直接上结果~ 仍旧是分为两个方案&#xff0c;1大1小。 经过人工神经网络计算并进行权…

element-ui alert 组件源码分享

今日简单分享 alert 组件源码实现&#xff0c;主要从以下四个方面来分享&#xff1a; 1、alert 组件的页面结构 2、alert 组件的属性 3、alert 组件的 slot 4、alert 组件的方法 一、alert 组件的页面结构 二、alert 组件的属性 2.1 title 属性&#xff0c;标题&#xff…

python爬虫+django新闻推荐系统可视化分析

1. 安装python3.7.0 2. 更新pip 控制台执行 python -m pip install -U pip 3. 安装依赖库 pip install -r requirements.txt 4. 更改mysql数据库配置 修改newsServer/settings.py中的数据库连接配置&#xff0c;比如修改下方PASSWORD密码为本机mysql密码&#xff1…

网络套接字补充——TCP网络编程

六、TCP网络编程 6.1IP地址字符串和整数之间的转换接口 //字符串转整数接口 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp); int inet_pton(int af, const char *strptr, …