当前位置: 首页 > news >正文

线程安全(重点)

文章目录

  • 一.线程安全的概念
    • 1.1 线程安全的概念
    • 1.2 线程不安全的原因
    • 1.3 解决线程不安全
  • 二.synchronized-monitor lock(监视器锁)
    • 2.1 synchronized的特性
      • (1)互斥
      • (2)刷新内存
      • (3)可重入
    • 2.2 synchronied使用方法
      • 1.直接修饰普通方法:
      • 2.修饰静态方法:
      • 3.修饰代码块:
  • 三.死锁
    • 3.1死锁的情况
    • 3.2 死锁的四个必要条件
      • 1.互斥使用
      • 2.不可抢占
      • 3.请求和保持
      • 4.循环等待
    • 3.3解决死锁的办法
  • 四.volatile 关键字
  • 五. wait和notify
    • 5.1 wait()方法
    • 5.2 notify()方法

一.线程安全的概念

先来看一段代码

class Counter{public int count = 0;public void add(){count++;}}
public class Thread14 {public static void main(String[] args) throws InterruptedException {Counter counter = new Counter();Thread t1  = new Thread(()->{for (int i = 0; i < 50000; i++) {counter.add();}});Thread t2 = new Thread(() ->{for (int i = 0; i < 50000; i++) {counter.add();}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = "+ counter.count);}
}

可以看到结果是不确定的

在这里插入图片描述
这里是引用

1.1 线程安全的概念

先来说一下非线程安全的概念:非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。
线程安全:如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

1.2 线程不安全的原因

先解释上述线代码程不安全的原因:
在这里插入图片描述

如果两个线程并发执行count++,此时就相当于两组load add save进行执行,此时不同的线程调度顺序就可能会产生一些结果上的差异

由于线程的抢占执行,导致当前执行到任意一个指令,线程都可能bei调度走,CPU让别的线程来执行
如下图:
在这里插入图片描述
导致下面的结果:
在这里插入图片描述
线程安全问题的原因:
1.抢占式执行,随机调度(根本原因)
2.代码结构:多个线程同时修改同一个变量
3.原子性(操作是非原子性,容易出现问题)
4.内存可见性问题(如一个线程读,一个线程改)
5.指令重排序

1.3 解决线程不安全

从原子性入手,通过加锁,把非原子的,转成"原子"的
在这里插入图片描述
加了synchronized之后,进入方法就会加锁,出了方法就会解锁,如果两个线程同时尝试加锁,此时一个能获取锁成功,另一个只能阻塞等待(BLOCKED),一直阻塞到刚才的线程解锁,当前线程才能加锁成功

二.synchronized-monitor lock(监视器锁)

2.1 synchronized的特性

(1)互斥

  • 进入sychronized修饰的代码块,相当于加锁
  • 退出sychronizde修饰的代码块,相当于解锁

(2)刷新内存

synchronized的工作过程:

1.获得互斥锁
2.从内存拷贝变量的最新副本到工作的内存
3.执行代码
4.将更改后的共享变量的值刷新到主内存
5.释放互斥锁

(3)可重入

synchronized同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题(自己可以再次获取自己的内部锁)
理解"把自己锁死"
一个线程没有释放锁,然后又尝试再次加锁
在这里插入图片描述
按照之前对于锁的设定,第二次加锁的时候,就会阻塞等待,而获取不到第一次的锁,就把自己锁死

2.2 synchronied使用方法

1.直接修饰普通方法:

锁的SynchronizedDemo1对象

public class SynchronizedDemo1 {public synchronized void methond() {}
}

2.修饰静态方法:

锁SynchronizedDemo2对象

public class SynchronizedDemo2 {public synchronized void methond() {}
}

3.修饰代码块:

明确指定锁哪个对象

public class SychronizedDemo{public void method(){sychronized(this){}}
}

锁类对象

public class SynchronizedDemo {public void method() {synchronized (SynchronizedDemo.class) {}}
}

三.死锁

3.1死锁的情况

1.一个线程,连续加锁两次,如果锁是不可重入锁,就会死锁
2.两个线程,两把锁,t1和t2各自先针对锁A和锁B加锁,在获取对方的锁

public class Thread15 {public static void main(String[] args) {Object lock1 = new Object();Object  lock2= new Object();Thread t1 = new Thread(()->{synchronized (lock1){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock2){System.out.println("t1把锁1和锁2都获得了");}}});Thread t2 = new Thread(()->{synchronized (lock2){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock1){System.out.println("t2把锁1和锁2都获得了");}};});t1.start();t2.start();}
}

在这里插入图片描述
3.多个线程,多把锁(相当于2的一般情况)

3.2 死锁的四个必要条件

1.互斥使用

线程1拿到了锁,线程2就须等着

2.不可抢占

线程1拿到锁A之后,必须是线程1主动释放

3.请求和保持

线程1拿到锁A之后,在尝试获取锁B,A这把锁还是保持的

4.循环等待

线程1尝试获取到锁A和锁B,线程2尝试获取锁B和锁A,线程1在获取B的时候等待线程2释放B,同时线程2 在获取A的时候等待线程1释放A

3.3解决死锁的办法

给锁编号,然后指定一个固定的顺序来加锁,任意线程加把锁,都让线程遵守上述顺序,此时循环等待自然破除

对于synchronied前三个条件都是锁的基本特性,我们只能对四修改

public class Thread15 {public static void main(String[] args) {Object lock1 = new Object();Object  lock2= new Object();Thread t1 = new Thread(()->{synchronized (lock1){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock2){System.out.println("t1把锁1和锁2都获得了");}}});Thread t2 = new Thread(()->{synchronized (lock1){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock2){System.out.println("t2把锁1和锁2都获得了");}};});t1.start();t2.start();}
}

在这里插入图片描述

四.volatile 关键字

volatile 和内存可见性问题密切相关

一个线程针对一个变量进行读取操作,同时另一个线程针对这个变量进行修改,此时读取到值,不一定是修改之后的值(归根结底是编译器/jvm在多线程下优化时产生了误判)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
使用汇编语言解释
1.load,把内存中flag的值,读取到寄存器
2.cmp把寄存器的值和0进行比较,根据比较结果,决定下一不执行.
由于load执行速度太慢(相比于cmp来说),再加上反复load的结果都一样,JVM就不在重复load判定没人改flag值,就只读取一次就好
而给flag加上volatile关键字,告诉编译器变量是"易变"的,不再进行优化

class MyCounter{volatile public int flag = 0;
}
public class Thread16 {public static void main(String[] args) {MyCounter myCounter = new MyCounter();Thread t1 = new Thread(() ->{while (myCounter.flag == 0){//循环体空着}System.out.println("t1循环结束");});Thread t2 = new Thread(() ->{Scanner scanner = new Scanner(System.in);System.out.println("请输入一个整数:");myCounter.flag = scanner.nextInt();});t1.start();t2.start();}
}

结果:
在这里插入图片描述

五. wait和notify

wait和notify可以协调线程之间的先后顺序

完成这个协调工作, 主要涉及到三个方法

  • wait() / wait(long timeout): 让当前线程进入等待状态.
  • notify() / notifyAll():唤醒在当前对象上等待的线程.

注意: wait, notify, notifyAll 都是 Object 类的方法

5.1 wait()方法

wait的操作
1.先释放锁
2.在阻塞等待
3.收到通知之后,重新获取锁,并且在获取锁后,继续往下执行

wait操作需要搭配synchorized来使用

public class Thread17 {public static void main(String[] args) throws InterruptedException {Object object = new Object();System.out.println("wait之前");object.wait();System.out.println("wait之后");}
}

无synchorized的情况
在这里插入图片描述
wait无参数版本,就是死等
wait带参数版本,指定了等待的最大时间

5.2 notify()方法

notify()方法是唤醒等待线程

  • 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 “先来后到”)

  • 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

notfiyAll()方法可以一次唤醒所有的等待线程

相关文章:

线程安全(重点)

文章目录一.线程安全的概念1.1 线程安全的概念1.2 线程不安全的原因1.3 解决线程不安全二.synchronized-monitor lock(监视器锁)2.1 synchronized的特性(1)互斥(2)刷新内存(3)可重入2.2 synchronied使用方法1.直接修饰普通方法:2.修饰静态方法:3.修饰代码块:三.死锁3.1死锁的情…...

软件测试面试找工作你必须知道的面试技巧(帮助超过100人成功通过面试)

目录 问题一&#xff1a;“请你自我介绍一下” 问题二&#xff1a;“谈谈你的家庭情况” 问题三&#xff1a;“你有什么业余爱好?” 问题四&#xff1a;“你最崇拜谁?” 问题五&#xff1a;“你的座右铭是什么?” 问题六&#xff1a;“谈谈你的缺点” 问题七&#xff…...

Python快速入门:类、文件操作、正则表达式

类、文件操作、正则表达式1. 类2. 文件操作3. 正则表达式1. 类 类是用来描述具有相同的属性和方法的集合&#xff0c;定义了该集合中每个对象共有的属性和方法&#xff0c;对象是类的实例&#xff0c;可以调用类的方法。 定义类时&#xff0c;如有父类&#xff0c;则写在类名…...

java-day01

程序就是有序指令的集合 cmd执行java程序&#xff0c;javac Test.java&#xff0c;java Test java技术平台&#xff1a; javaSE标准版&#xff0c;javaEE企业版&#xff0c;javaME小型版 java语言面向对象的&#xff08;oop&#xff09;&#xff0c;java跨平台性的&#xff08;…...

玩转 Node.js 集群

一、介绍 Node 在 v0.8 时直接引入了 cluster 模块&#xff0c;用以解决多核 CPU 的利用率问题&#xff0c;同时也提供了较完善的 API&#xff0c;用以处理进程的健壮性问题。 cluster 模块调用 fork 方法来创建子进程&#xff0c;该方法与 child_process 中的 fork 是同一个…...

Day909.MySQL 不同的自增 id 达到上限以后的行为 -MySQL实战

MySQL 不同的自增 id 达到上限以后的行为 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于MySQL 不同的自增 id 达到上限以后的行为的内容。 MySQL 里有很多自增的 id&#xff0c;每个自增 id 都是定义了初始值&#xff0c;然后不停地往上加步长。 虽然自然数是没有…...

JVM学习.01 内存模型

1、前言对于C、C程序员来说&#xff0c;在内存管理领域&#xff0c;他们拥有对象的“所有权”。从对象建立到内存分配&#xff0c;不仅需要照顾到对象的生&#xff0c;还得照顾到对象的消亡。背负着每个对象生命开始到结束的维护和管理责任。对于JAVA程序来说&#xff0c;因为J…...

R+VIC模型应用及未来气候变化模型预测

RVIC模型融合实践技术应用及未来气候变化模型预测在气候变化问题日益严重的今天&#xff0c;水文模型在防洪规划&#xff0c;未来预测等方面发挥着不可替代的重要作用。目前&#xff0c;无论是工程实践或是科学研究中都存在很多著名的水文模型如SWAT/HSPF/HEC-HMS等。虽然&…...

搞懂vue 的 render 函数, 并使用

render函数是什么 简单的说&#xff0c;在vue中我们使用模板HTML语法组建页面的&#xff0c;使用render函数我们可以用js语言来构建DOM 因为vue是虚拟DOM&#xff0c;所以在拿到template模板时也要转译成VNode(虚拟节点)的函数&#xff0c;而用render函数构建DOM&#xff0c;vu…...

【Linux】GDB的安装与使用

安装安装gdb的具体步骤如下&#xff1a;1、查看当前gdb安装情况rpm -qa | grep gdb如果有&#xff0c;则可以先删除&#xff1a;rpm -e --nodeps 文件名如果没有&#xff0c;则进行下一步。2、下载gdb源码包或者直接apt安装。apt命令安装&#xff1a;sudo apt install gdb源码包…...

MySQL索引特性

文章目录为什么要有索引&#xff1f;认识磁盘磁盘的结构磁盘的盘片结构定位扇区磁盘随机访问 (Random Access)与连续访问 (Sequential Access)MySQL与磁盘交互索引的理解测试主键索引索引的原理索引结构是否可以使用其他数据结构B树 vs B树聚簇索引 vs 非聚簇索引为什么要有索引…...

Python 面向对象编程——类定义与对象

<类定义与对象声明> 面向对象最重要的概念就是类&#xff08;Class&#xff09;和实例&#xff08;Instance&#xff09;&#xff0c;必须牢记类是抽象的模板&#xff0c;比如Student类&#xff0c;而实例是根据类创建出来的一个个具体的“对象”&#xff0c;每个对象都拥…...

基于 Apache Flink 的实时计算数据流业务引擎在京东零售的实践和落地

摘要&#xff1a;本文整理自京东零售-技术研发与数据中心张颖&闫莉刚在 ApacheCon Asia 2022 的分享。内容主要包括五个方面&#xff1a; 京东零售实时计算的现状实时计算框架场景优化&#xff1a;TopN场景优化&#xff1a;动线分析场景优化&#xff1a;FLINK 一站式机器学…...

【JavaEE】如何将JavaWeb项目部署到Linux云服务器?

写在前面 大家好&#xff0c;我是黄小黄。不久前&#xff0c;我们基于 servlet 和 jdbc 完善了博客系统。本文将以该系统为例&#xff0c;演示如何将博客系统部署到 Linux 云服务器。 博客系统传送门&#xff1a; 【JavaEE】前后端分离实现博客系统&#xff08;页面构建&#…...

Mysql常用命令

mysql连接&#xff1a; [roothost]# mysql -u root -p Enter password:******创建数据库&#xff1a; CREATE DATABASE 数据库名&#xff1b; 删除数据库&#xff1a; drop database 数据库名; 使用mysqladmin删除数据库&#xff1a; [roothost]# mysqladmin -u root -p dr…...

【洛谷刷题】蓝桥杯专题突破-深度优先搜索-dfs(4)

目录 写在前面&#xff1a; 题目&#xff1a;P1149 [NOIP2008 提高组] 火柴棒等式 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 解题思路&#xff1a; …...

在Win10以及SDK为33的环境下——小米便签项目的搭建

文章目录0. 我的操作系统和开发环境1. 相关文件下载&#xff1a;2. import project&#xff1a;2.1 用import project导入项目3. make project&#xff1a;3.1 AS中的命令行乱码问题:3.2 依赖库缺失问题:3.3 关于targetSdkVersion3.4 关于Missing URL3.5 关于Manifest merger f…...

FPGA纯verilog实现RIFFA的PCIE通信,提供工程源码和软件驱动

目录1、前言2、RIFFA简介RIFFA概述RIFFA架构RIFFA驱动3、vivado工程详解4、上板调试验证并演示5、福利&#xff1a;工程代码的获取1、前言 PCIE是目前速率很高的外部板卡与CPU通信的方案之一&#xff0c;广泛应用于电脑主板与外部板卡的通讯&#xff0c;PCIE协议极其复杂&…...

Linux网络配置

文章目录一、Linux网络配置原理图二、查看网络IP和网关ping测试主机之间网络连通性三、linux网络环境配置第一种方法(自动获取)第二种方法(指定ip)四、设置主机名和hosts映射设置主机名设置hosts映射五、主机名解析过程分析(Hosts、DNS)Hosts是什么DNS一、Linux网络配置原理图 …...

【Java学习笔记】多线程与线程池

多线程与线程池一、多线程安全与应用1、程序、进程与线程的关系2、创建多线程的三种方式&#xff08;1&#xff09;继承Thread类创建线程【不推荐】&#xff08;2&#xff09;实现Runnable接口创建线程&#xff08;3&#xff09;Callable接口创建线程3、线程的生命周期4、初识线…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...

Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解

文章目录 一、开启慢查询日志&#xff0c;定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...