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

news2024/10/28 4:35:12

目录

  • 一、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.coloradmin.cn/o/2225179.html

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

相关文章

AI编译器与TVM

由于AI芯片的特殊性和高度定制化&#xff0c;为了兼容硬件的多样性&#xff0c;AI模型必须能被高效地映射到各种AI芯片上。AI编译器将深度学习框架描述的AI模型作为输入&#xff0c;将为各种AI芯片生成的优化代码作为输出。AI编译器的目标是通过编译优化的方法将深度学习框架产…

Git的原理和使用(六)

本文主要讲解企业级开发模型 1. 引入 交付软件的流程&#xff1a;开发->测试->发布上线 上面三个过程可以详细划分为一下过程&#xff1a;规划、编码、构建、测试、发 布、部署和维护 最初&#xff0c;程序⽐较简单&#xff0c;⼯作量不⼤&#xff0c;程序员⼀个⼈可以完…

2025 - AI人工智能药物设计 - 中药网络药理学和毒理学的研究

中药网络药理学和毒理学的研究 TCMSP&#xff1a;https://old.tcmsp-e.com/tcmsp.php 然后去pubchem选择&#xff1a;输入Molecule Name 然后进行匹配&#xff1a;得到了smiles 再次通过smiles&#xff1a;COC1C(CC(C2C1OC(CC2O)C3CCCCC3)O)O 然后再次输入&#xff1a;http…

单体架构VS微服务架构

单体架构&#xff1a;一个包含有所有功能的应用程序 优点&#xff1a;架构简单、开发部署简单缺点&#xff1a;复杂性高、业务功能多、部署慢、扩展差、技术升级困难 如上示意图&#xff0c;应用前端页面&#xff0c;后台所有模块功能都放在一个应用程序中&#xff0c;并部署在…

「C/C++」C++标准库之#include<fstream>文件流

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

shodan2---清风

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

MedSAM微调版,自动生成 Prompt 嵌入实现图像分割!

最近提出的Segment Anything Model (SAM)等基础模型在图像分割任务上取得了显著的成果。 然而&#xff0c;这些模型通常需要通过人工设计的 Prompt &#xff08;如边界框&#xff09;进行用户交互&#xff0c;这限制了它们的部署到下游任务。 将这些模型适应到具有完全 Token 数…

Arduino Uno 同时控制多路舵机

Arduino Uno同时控制4个舵机 舵机可以在0~180度内指定角度的控制。常用于航模、机器人、遥控玩具等物品,然而,很多时候要一次性控制多个舵机,今天以控制4个舵机为例进行说明 接线方式如下图: 舵机的信号线分别接A0,A1,A2,A3。控制舵机从0旋转到180度,再由180度旋转到0度,…

从0开始深度学习(18)——层和块

1 层和块 1.1层 层是神经网络的基本组成单位。每一层由多个神经元&#xff08;或单元&#xff09;组成&#xff0c;这些神经元在前一层的输出上执行某种计算&#xff0c;并将结果传递给下一层。根据功能&#xff0c;层可以分为以下几种类型&#xff1a; 输入层&#xff08;I…

《决策思维:人人必备的决策口袋书》

本书干货很多&#xff0c;十分值得一读。但受众不是一线员工与一线管理者&#xff0c;更多的倾向于管理者的管理者。一线员工读完的最大收获是可以理解老板的决策逻辑与思维方式&#xff0c;便于更好的去做执行。同时&#xff0c;还能帮助判断老板的决策是否正确&#xff0c;是…

【Android】view的基础知识

文章目录 View与ViewGroupView位置参数View的滑动1. scrollTo与scrollBy2. 属性动画ObjectAnimatorViewPropertyAnimator 3. LayoutParams&#xff08;布局参数&#xff09;layout方法offsetLeftAndRight View的弹性滑动1. Scroller 类2. 动画&#xff08;ObjectAnimator&#…

SYN590RL 300MHz至450MHz ASK接收机芯片IC

一般描述 SYN590RL是赛诺克全新开发设计的一款宽电压范围,低功耗,高性能,无需外置AGC电容&#xff0c;灵敏度达到典型-110dBm&#xff0c;300MHz”450MHz 频率范围应用的单芯片ASK或OOK射频接收器。 SYN59ORL是一款典型的即插即用型单片高集成度无线接收器&…

Spring Boot实现的动态化酒店住宿管理系统

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理酒店客房管理系统的相关信息成为必然。开发…

pytorch的标签平滑介绍

什么是标签平滑(Label Smoothing)? 标签平滑(Label Smoothing)是一种正则化技术,旨在防止模型过度自信(即输出的概率分布过于“尖锐”)。在分类任务中,标准的目标标签是one-hot编码,也就是正确类别的概率为 1,其他类别的概率为 0。而标签平滑通过将正确类别的概率从…

绝了,这款播放器让发烧友疯狂种草,堪称音乐神器

作为音乐爱好者的不二之选&#xff0c;foobar2000凭借其卓越的音频处理能力&#xff0c;在Windows系统用户中树立了极高的声誉。这款轻量级播放器的设计理念是将极致的性能与个性化完美结合。用户可以根据自己的使用习惯&#xff0c;打造出独具特色的播放界面和操作流程。在音频…

【redis】初识非关系型数据库——redis

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 初识 Redis Redis是⼀种基于键值对&#xff08;key-value&#xff09;的NoSQL数据库&#xff0c;与很多键值对数据库不同的是&#xff0c;Redis 中的值可以是由string&#xff08;字符串&#xff09;、hash&#xff0…

html 登入界面,用户注册界面相关的标签及案例

案例效果图 以上界面的完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</titl…

Python浪漫之画星星

效果图&#xff08;动态的哦&#xff01;&#xff09;&#xff1a; 完整代码&#xff08;上教程&#xff09;&#xff1a; import turtle import random import time # 导入time模块# 创建一个画布 screen turtle.Screen() screen.bgcolor("red")# 创建一个海龟&a…

【学术论文投稿】Windows11开发指南:打造卓越应用的必备攻略

【IEEE出版南方科技大学】第十一届电气工程与自动化国际会议&#xff08;IFEEA 2024)_艾思科蓝_学术一站式服务平台 更多学术会议论文投稿请看&#xff1a;https://ais.cn/u/nuyAF3 目录 引言 一、Windows11开发环境搭建 二、Windows11关键新特性 三、Windows11设计指南 …

Android 自定义 Dialog 实现列表 单选,多选,搜索

前言 在Android开发中&#xff0c;通过对话框让用户选择&#xff0c;筛选信息是很方便也很常见的操作。本文详细介绍了如何使用自定义 Dialog、RecyclerView 以及自定义搜索框 来实现选中状态和用户交互&#xff0c;文中大本分代码都有明确注释&#xff0c;主打一个简单明了&a…