Java 多线程共享数据引发的问题

news/2024/10/17 13:30:07/

一、多线程并发情况下,线程不安全​​

1、使用多线程实现银行取钱​​

package theads;/*** @ClassName: TestBank* @Description: TODO* @Author: HLX* @date: 2023/5/29 14:53* @Version: V1.0*//*** 线程不安全: 取钱* <p>* 逻辑:* 连取两次  则为负数 -70;* 各自的口袋的钱是没问题的。*/
public class TestBank {public static void main(String[] args) {//账户上有100元生活费用Account account = new Account(100, "生活费用");System.out.println(account);//线程MyBank t1 = new MyBank("张三", account, 80);MyBank t2 = new MyBank("刘芳", account, 90);MyBank t3 = new MyBank("汪峰", account, 50);t1.start();t2.start();t3.start();}
}//银行账户
class Account {private int money; //金额private String name; //备注public Account(int money, String name) {this.money = money;this.name = name;}@Overridepublic String toString() {return name + "入账" + money + "元";}public Account() {}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}//线程
class MyBank extends Thread {private Account account; //账户private int takeMoney;//取钱//构造方法public MyBank(String name, Account account, int takeMoney) {super(name);this.account = account;this.takeMoney = takeMoney;}@Overridepublic void run() {try {Thread.sleep(1000);         // 模拟延迟网络时间} catch (InterruptedException e) {e.printStackTrace();}//从账户上取取钱if (account.getMoney() - takeMoney < 0) {return;}try {Thread.sleep(1000);         // 如果不堵塞的话,速度太快。看不到问题} catch (InterruptedException e) {e.printStackTrace();}
//        设置账户上剩余的钱account.setMoney(account.getMoney() - takeMoney);System.out.println(this.getName() + "取钱:" + takeMoney + "元,余额为:" + account.getMoney() + "元");}
}

从运行的效果来看:
余额竟然会出现-40的情况,原因是有100现金,
汪峰取50,剩-40
刘芳取90,剩-40
张三取80,剩-40
也就相当于那个判断没起作用。这就是线程不安全。

 原因是:3个进程同时操作这个唯一的资源,就会出现线程不安全的情况。

 二、线程不安全解密:内存可见性

 1、 java内存模型

   (1)Java的内存模型分为主内存工作内存(线程的)

   (2)Java内存模型规定了所有的变量都存储在主内存中,每个线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。

   (3)线程对变量的所有操作(读取,赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。有一个关键字是 ​​volatile​​, volatile可以保证任何情况下变量的可见性,是不是就是直接读主内存呢,事实上,volatile变量依然有工作内存的拷贝,但是它的操作顺序比较特殊,会每次都从主内存重新加载,所以你会看到每次volatile读取到的都是最新的值。

   (4)不同的线程也无法访问其他线程的工作内存中的变量,线程间变量值的传递需要通过主内存来完成。

2、线程不安全说明:

      当多个线程操作同一个对象或者同一个资源时(如取钱),每个线程会把资源从java主内存中 读取一份到自己的工作内存中,进行操作,操作后,在将结果写到java主内存中。但是,因为有多个线程在同时操作,就要多个工作内存去读和写,假如有A,B 两个进程,都读取了一份资源到自己的工作内存中, B内存可能没被更新到主内存去。导致A线程或者其他内存 从主内存拷贝数据到自己的工作区时,拷贝的不是最新的数据。这就是 ​​内存可见性问题​​。从而导致线程不安全问题。
 

3、线程安全与不安全

     线程不安全:是不提供加锁机制保护,有可能出现多个线程先后更改数据造成所得到的数据是​​脏数据​​

      线程安全:指多个线程在执行同一段代码的时候采用​​加锁机制​​,使每次的执行结果和单线程执行的结果都是一样的,不存在执行程序时出现意外结果。
 

三、解决线程安全性问题

    1、 线程同步概念

        当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线      程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。

     2、使用线程同步方法和同步块

    (1) 同步方法语法

访问修饰符 synchronized 返回类型 方法名(参数列表){……}

or

synchronized 访问修饰符 返回类型 方法名(参数列表){……}

   (2) 同步块语法

   synchronized (obj){    },  // obj可以是任何对象,但是推荐使用共享资源作为同步监视器

  使用多线程同步块实现银行取钱​​

package theads;/*** @ClassName: TestBank* @Description: TODO* @Author: HLX* @date: 2023/5/29 14:53* @Version: V1.0*/public class TestBank2 {public static void main(String[] args) {//账户上有200元生活费用Account2 account2 = new Account2(200, "生活费用");System.out.println(account2);MyBank2 t1 = new MyBank2("张三", account2, 80);MyBank2 t2 = new MyBank2("刘芳", account2, 90);MyBank2 t3 = new MyBank2("汪峰", account2, 10);t1.start();t2.start();t3.start();}
}//银行账户
class Account2 {private int money; //金额private String name; //备注public Account2(int money, String name) {this.money = money;this.name = name;}@Overridepublic String toString() {return name + "入账" + money + "元";}public Account2() {}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}//线程
class MyBank2 extends Thread {private Account2 account; //账户private int takeMoney;//取钱//构造方法public MyBank2(String name, Account2 account, int takeMoney) {super(name);this.account = account;this.takeMoney = takeMoney;}//缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。@Overridepublic void run() {/*** 提高性能*/if (account.getMoney() <= 0) {return;}//目标锁定account账户//同步块synchronized (account) {//从账户上取取钱if (account.getMoney() - takeMoney < 0) {return;}try {Thread.sleep(1000);         // 模拟网络延时} catch (InterruptedException e) {e.printStackTrace();}
//        设置账户上剩余的钱account.setMoney(account.getMoney() - takeMoney);System.out.println(this.getName() + "取钱:" + takeMoney + "元,|__余额为:" + account.getMoney() + "元");}}
}

  • 同一时刻只能有一个线程进入synchronized(this)同步代码块
  • 当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
  • 当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码 

 

 四、synchronized 的范围问题,优化效率

(1) 同步方法 所锁住的范围太大,线程安全,影响效率。
(2) 同步块 所锁住的范围太小,锁不住,线程不安全。

 


http://www.ppmy.cn/news/107337.html

相关文章

洗地机充电底座语音芯片选型?NV040DS语音芯片

一、洗地机语音提示功能的价值 洗地机充电底座加入语音提示功能&#xff0c;主要是为了提高洗地机的智能化程度和使用便利性&#xff01; 1. 提高使用效率&#xff1a;底座语音提示充电状态可以使用户更方便地掌握底座电量和洗地机的使用情况&#xff0c;从而更快捷地对底座进…

算法---边界着色

题目 给你一个大小为 m x n 的整数矩阵 grid &#xff0c;表示一个网格。另给你三个整数 row、col 和 color 。网格中的每个值表示该位置处的网格块的颜色。 两个网格块属于同一 连通分量 需满足下述全部条件&#xff1a; 两个网格块颜色相同 在上、下、左、右任意一个方向上…

SSH配置密钥免密码登录

1.生成key 在本地主机输入以下命令 [rootlocalhost ~]# ssh-keygen 一直回车 Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory /root/.ssh. Enter passphrase (empty for no passphrase): Enter s…

信创办公–基于WPS的EXCEL最佳实践系列 (规整数据摆放)

信创办公–基于WPS的EXCEL最佳实践系列 &#xff08;规整数据摆放&#xff09; 目录 应用背景操作步骤1、数据排序2、例如&#xff1a;职务按照 经理-主任-职员 排序3、排列第二种方法4、实操案例5、案例练习一方法一&#xff1a;通过公式函数增加辅助列方法二&#xff1a;用辅…

变电站与智能变电站

变电站 概念&#xff1a;变电站是指电力系统中对电业和电流进行变换&#xff0c;接受电能和分配电能的场所。在发电厂内的变电站是升压变压站&#xff0c;其作用是将发电机发出的电能升压后馈送到高压电网中。 分类 一类变电站。是指交流特高压站&#xff0c;核电、大型能源…

LeetCode——可被三整除的偶数的平均值

#全国科技者工作日—为创新和未来而努力# 目录 1、题目 2、题目解读 3、代码 1、题目 2455. 可被三整除的偶数的平均值 - 力扣&#xff08;Leetcode&#xff09; 给你一个由正整数组成的整数数组 nums &#xff0c;返回其中可被 3 整除的所有偶数的平均值。 注意&#xff…

IDS 和 IPS 日志监控

什么是IDS/IPS 入侵检测系统 &#xff08;IDS&#xff09; 和入侵防御系统 &#xff08;IPS&#xff09; 是监视组织网络中的流量以检测和防止恶意活动和策略违规的网络组件。 入侵检测系统&#xff08;IDS&#xff09;和入侵防御系统&#xff08;IPS&#xff09;可以说是企业…

财务共享五大价值助力央企构建世界一流财务管理体系

如果说小微企业是我国市场经济的毛细血管的话&#xff0c;那么央企就是承载着我国市场发展的主动脉。以规模为导向来看&#xff0c;央企完成了第一次长征&#xff0c;但央企在盈利能力、市场份额、行业地位、专利技术与优势、品牌影响力、市值管理、标准和规则制定话语权等软实…