【JavaEE】【多线程】volatile,wait/notify

目录

  • 一、volatile关键字
    • 1.1 内存可见性
    • 1.2 volatile解决内存可见性问题
  • 二、wait和notify
    • 2.1 wait
    • 2.2 notify
    • 2.3 使用例子
      • 2.3.1 例子1
      • 2.3.2 例子二

一、volatile关键字

volatile可以保证内存可见性,只能修饰变量。

1.1 内存可见性

在前面介绍线程不安全原因时介绍到了,在Java中有JMM (Java Memory Model)(Java内存模型)来介绍。

计算机运行代码/程序的时候,访问数据常常要从内存中访问(定义变量时变量就储存在内存中),
然而CPU从内存中读取数据相比于从寄存器中读取数据要慢上很多(几千上万倍),CPU在进行读/写内存的时候速度就会降低。

为了解决这种问题,提高效率,编译器就可能会对代码优化,把一些本来要读取内存的操作,优化为读取寄存器,减少读取内存的次数。这就会导致内存可见性问题。

例如以下代码输入一个不为0的数,本应该打印“threade1结束”,但是并没有。

import java.util.Scanner;
public class Demo {public static int isQuite = 0;public static void main(String[] args) {Thread thread1 = new Thread(() -> {while(isQuite == 0) {}System.out.println("threade1结束");}) ;Thread thread2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);isQuite = scanner.nextInt();});thread1.start();thread2.start();}
}

以上述代码讲解:
在thread1中while先读取isQuite的值,在进行比较,然而编译器/JVM发现多次得到的isQuite都是0,这个线程也没有修改isQuite操作,然后编译器/JVM就大胆优化只进行第一次的读取isQuite操作,后续直接从寄存器里面读取。

其实编译器/JVM进行优化是不可控的,如果在while循环里面加上sleep,sleep的时间够久了,已经够进行读取操作,可能就不会优化了。

1.2 volatile解决内存可见性问题

如上诉代码,我们直接在isQuite加上volatile修饰,就告诉编译器/JVM不要进行优化,就可以解决问题。

volatile可以解决内存可见性问题,解决不了原子性问题。

import java.util.Scanner;
public class Demo {volatile public static int isQuite = 0;public static void main(String[] args) {Thread thread1 = new Thread(() -> {while(isQuite == 0) {}System.out.println("threade1结束");}) ;Thread thread2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);isQuite = scanner.nextInt();});thread1.start();thread2.start();}
}

二、wait和notify

在多线程中一个重要的机制是协调各个线程之间的调度顺序,在操作系统是随机调度。
在前面我们介绍了等待一个线程使用join,但是使用join就要等到调用线程结束。而wait不用,wait和notify就是专门协调线程执行逻辑的顺序的。
wait和notify是Object类的成员方法,也就是每一个对象都有。

wait:等待,让指定线程进入阻塞状态。
notify:通知,唤醒对应进入阻塞状态的线程。

2.1 wait

线程饿死/饿死:线程恶死就是指当多个线程竞争一把锁的时候,当线程1拿到了锁,释放锁之后,又由于操作系统的随机调度再次多次让线程1拿到锁,其他线程多次没拿到锁处于阻塞状态,没分配到CPU资源。就相当于鸟妈妈给小鸟喂食,多次喂食都是给一只小鸟,那么其它小鸟就处于饥饿/饿死状态。

我们可以使用wait来避免线程饥饿,当线程拿到锁发现时机不成熟的时候,就可以使用wait让线程进入阻塞状态,等待唤醒。

语法:

synchronized(锁对象) {锁对象.wait();
}

注意事项:

  • wait()必须搭配synchronized使用:因为wait()的机制就是先释放锁对象的锁,然后等待唤醒在加锁继续执行剩下逻辑。
  • 如果对象没有处于加锁状态,就会抛出IllegalMonitorStateException(非法锁状态异常)。
  • wait会抛InterruptedException
  • wait也有含时间版本,超过时间自动唤醒。

2.2 notify

notify就是唤醒wait。
语法:

synchronized(锁对象) {锁对象.notify();
}

注意事项:

  • notify必须在wait后面执行才能唤醒wait;
  • notify和要唤醒的wait要是同一个锁对象;
  • 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 “先来后到”)。
  • 当有多个线程等待时,可以使用notifyAll()来唤醒所有,但是实际上还是一个一个唤醒。

2.3 使用例子

2.3.1 例子1

题目:使用多线程来打印ABC,一个线程一个字母,打印10个ABC。

解析:线程1唤醒线程2,线程2唤醒线程3,线程3唤醒线程1。主线程中保证先唤醒一下线程1即可。

代码:

public class Demo {public static void main(String[] args) throws InterruptedException {Object block1 = new Object();Object block2 = new Object();Object block3 = new Object();Thread thread1 = new Thread(() -> {try {for (int i = 0; i < 10; i++) {synchronized (block1) {block1.wait();}System.out.print("A");synchronized (block2) {block2.notify();}}}catch (InterruptedException e) {e.printStackTrace();}});Thread thread2 = new Thread(() -> {try {for (int i = 0; i < 10; i++) {synchronized (block2) {block2.wait();}System.out.print("B");synchronized (block3) {block3.notify();}}}catch (InterruptedException e) {e.printStackTrace();}});Thread thread3 = new Thread(() -> {try {for (int i = 0; i < 10; i++) {synchronized (block3) {block3.wait();}System.out.print("C"+" ");synchronized (block1) {block1.notify();}}}catch (InterruptedException e) {e.printStackTrace();}});thread1.start();thread2.start();thread3.start();Thread.sleep(1000);synchronized (block1) {block1.notify();}}
}

2.3.2 例子二

题目:有三个线程,线程名称分别为:a,b,c。
每个线程打印自己的名称。
需要让他们同时启动,并按 c,b,a的顺序打印。

代码:

public class Demo {public static void main(String[] args) throws InterruptedException {Object block1 = new Object();Object block2 = new Object();Object block3 = new Object();Thread a = new Thread( () -> {Thread.currentThread().setName("a");try{synchronized (block1) {block1.wait();}System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}});Thread b = new Thread(() -> {Thread.currentThread().setName("b");try{synchronized (block2) {block2.wait();}System.out.print(Thread.currentThread().getName()+",");} catch (InterruptedException e) {e.printStackTrace();}synchronized (block1) {block1.notify();}});Thread c = new Thread(() -> {Thread.currentThread().setName("c");try{synchronized (block3) {block3.wait();}System.out.print(Thread.currentThread().getName()+",");synchronized (block2) {block2.notify();}} catch (InterruptedException e) {e.printStackTrace();}});a.start();b.start();c.start();Thread.sleep(1000);synchronized (block3) {block3.notify();}}
}

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

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

相关文章

大数据开发扩展shell 笔记

大数据开发扩展shell 此笔记来自尚硅谷 学习目标 1 熟悉shell脚本的原理和使用 2 熟悉shell的编程语法 第一节 Shell概述 1&#xff09;Linux提供的Shell解析器有&#xff1a; [atguiguhadoop101 ~]$ cat /etc/shells /bin/sh/bin/bash/sbin/nologin/bin/dash/bin/tcsh/b…

JCSA-Journal of Consumer Affairs

文章目录 一、征稿简介二、重要信息三、服务简述四、投稿须知五、联系咨询 一、征稿简介 二、重要信息 期刊官网&#xff1a;https://ais.cn/u/3eEJNv 三、服务简述 Journal of Consumer Affairs由美国消费者利益委员会&#xff08;ACCI&#xff09;拥有&#xff0c;成立于1…

淘宝商品详情的“侦探游戏”:如何用API接口揭开数据的面纱

在这个充满神秘数据的电商世界里&#xff0c;淘宝商品详情就像是一个个隐藏的宝藏&#xff0c;等待着我们去发掘。而API接口&#xff0c;就是我们的“侦探工具”&#xff0c;帮助我们快速揭开这些宝藏的面纱。今天&#xff0c;我们就来一场幽默的“侦探游戏”&#xff0c;看看如…

炒股VS炒CSGO游戏装备,哪个更好做

这个项目&#xff0c;赚个10%都是要被嫌弃的 虽然天天都在抒发自己对股市的看法&#xff0c;但自己自始至终也没有买进任何一支股票。之所以对这个话题感兴趣&#xff0c;着实是因为手上的游戏搬砖项目也是国际性买卖&#xff0c;跟国际形势&#xff0c;国际汇率挂钩&#xff…

C++线程池手写实现

1.Thread类的封装 封装Thread类&#xff0c;使其可以直接在外部调用对象的start,detach,join和cancel等方法来实现对线程的操作 1.1代码 //Thread.h// // Created by crab on 2024/10/20. //#ifndef THREAD_H #define THREAD_H#include <pthread.h>class Thread { pub…

使用 Docker 管理完整项目:Java、Vue、Redis 和 Nginx 的一站式部署

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…

[大模型学习推理]资料

https://juejin.cn/post/7353963878541361192 lancedb是个不错的数据库&#xff0c;有很多学习资料 https://github.com/lancedb/vectordb-recipes/tree/main/tutorials/Multi-Head-RAG-from-Scratch 博主讲了很多讲解&#xff0c;可以参考 https://juejin.cn/post/7362789…

js纯操作dom版购物车(实现购物车功能)

代码&#xff1a; <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title>&l…

Linux 进程间通信_匿名管道

1.程间通信目的 : 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享同样的资源。 通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它&#xff08;它们&#xff09;发生了某种事件&#xff08;如…

vue3+vite 部署npm 包

公司需要所以研究了一下怎么部署安装&#xff0c;比较简单 先下载个vue项目 用vue脚手架安装vue3项目 删掉App.vue 中的其它组件 npm create vuelatest 开始写自定义组件 新建一个el-text 组件, name是重点&#xff0c;vue3中也得这么导出name属性 <script> export de…

JAVA-----线程池

目录 1.简单理解一下线程池 2.线程池参数 3.Executors ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 1.简单理解一下线程池 线程池&#xff0c;就是用来存储线程的&#xff0c;然后如果有任务传进线程池…

PortQry下载安装使用教程(超详细),Windows测试UDP端口

《网络安全自学教程》 PortQry是微软官方提供的一款TCP/IP连接「排障工具」&#xff0c;用来「检查」TCP/UDP「端口状态」。 平时检查端口状态&#xff0c;最常用的是telnet&#xff0c;但它是基于TCP协议的&#xff0c;无法检测「UDP端口」&#xff0c;这篇文章教大家如何在W…

Flink-cdc Schema Evolution 详解

Flink-cdc Schema Evolution 详解 github原文 glimpse flink-cdc-3 glimpse 源码基于 ~/project/flink_src/flink-cdc master !4 ❯ git remote -v origin https://github.com/apache/flink-cdc.git (fetch) origin https://github.com/apache/flink-cdc.git (push) ~/p…

介绍一款Java开发的企业接口管理系统和开放平台

介绍一款Java开发的企业接口管理系统和开放平台&#xff0c;YesApi接口管理平台Java版本。该系统基于Java开发&#xff0c;可以帮助企业进行统一接口管理、API接口开放&#xff0c;以及用于PaaS系统和SaaS产品平台的快速二次开发和搭建。 一、系统概述 YesApi接口大师&#x…

ClickHouse 5节点集群安装

ClickHouse 5节点集群安装 在此架构中&#xff0c;配置了五台服务器。其中两个用于托管数据副本。其他三台服务器用于协调数据的复制。在此示例中&#xff0c;我们将创建一个数据库和表&#xff0c;将使用 ReplicatedMergeTree 表引擎在两个数据节点之间复制该数据库和表。 官…

RHCE作业二

1.要求&#xff1a; 配置nginx服务通过ip访问多网站 2. 1关闭防火墙 2创建ip 3配置 4创建文件 5测试

logback 如何将日志输出到文件

如何作 将日志输出到文件需要使用 RollingFileAppender&#xff0c;该 Appender 必须定义 rollingPolicy &#xff0c;另外 rollingPollicy 下必须定义 fileNamePattern 和 encoder <appender name"fileAppender" class"ch.qos.logback.core.rolling.Rollin…

二、Spring的执行流程

文章目录 1. spring的初始化过程1.1 ClassPathXmlApplicationContext的构造方法1.2 refresh方法&#xff08;核心流程&#xff09;1.2.1 prepareRefresh() 方法1.2.2 obtainFreshBeanFactory() 方法1.2.3 prepareBeanFactory() 方法1.2.4 invokeBeanFactoryPostProcessors() 方…

shodan2---清风

注&#xff1a;本文章源于泷羽SEC&#xff0c;如有侵权请联系我&#xff0c;违规必删 学习请认准泷羽SEC学习视频:https://space.bilibili.com/350329294 实验一&#xff1a;search 存在CVE-2019-0708的网络设备 CVE - 2019 - 0708**漏洞&#xff1a;** 该漏洞存在于远程桌面…

解读数字化转型的敏捷架构:从理论到实践的深度分析

在当今数字经济的推动下&#xff0c;企业要在瞬息万变的市场中保持竞争力&#xff0c;数字化转型已经不再是一种选择&#xff0c;而是不可避免的战略需求。然而&#xff0c;企业如何从理论到实践进行有效的转型&#xff0c;尤其是在复杂的技术环境中&#xff0c;如何通过正确的…