Java多线程--线程安全问题练习题
文章目录
- (1)练习题1
- (2)练习题2
- (3)练习题3
现在咱们线程一共说了这么几件事情,如下:
具体文章见专栏。
接下来看几个练习题吧。
(1)练习题1
🌋题目描述
【新年倒计时】
模拟新年倒计时,每隔1秒输出一个数字,依次输出10,9,8…1,最后输出:新年快乐!
🍰分析
题目中没有说要造分线程,那我们可以直接放到主线程里面,也是可以的。
直接写一个for循环遍历即可,如下:
public class HappyNewYear {public static void main(String[] args) {for (int i = 10; i >=1 ; i--) {System.out.println(i);}}
}
然后sleep,让它一秒钟输出一下。如下:

记得处理一下异常:

这里只能用try-catch处理,因为sleep抛出的方法是编译时异常,而且父类没有抛出,不能使用throws的方式。
🌱代码
package yuyi03;/*** ClassName: HappyNewYear* Package: yuyi03* Description:* 模拟新年倒计时,每隔1秒输出一个数字,依次输出10,9,8......1,最后输出:新年快乐!* @Author 雨翼轻尘* @Create 2024/1/30 0030 13:03*/
public class HappyNewYear {public static void main(String[] args) {for (int i = 10; i >=0 ; i--) {try {Thread.sleep(1000); //睡1s} catch (InterruptedException e) {e.printStackTrace();}if(i>0){System.out.println(i);}else {System.out.println("HappyNewYear!");}}}
}
🍺输出结果

(2)练习题2
🌋题目描述
关于Thread.sleep()方法的一个面试题:
如下的代码中sleep()执行后,到底是哪个线程进入阻塞状态了呢?
🌱代码
package yuyi03;/*** ClassName: ThreadTest* Package: yuyi03* Description:* 如下的代码中sleep()执行后,到底是哪个线程进入阻塞状态了呢?* @Author 雨翼轻尘* @Create 2024/1/30 0030 13:16*/public class ThreadTest {public static void main(String[] args) {// 创建线程对象MyThread t = new MyThread();t.setName("线程1");t.start();// 调用sleep方法try {t.sleep(1000 * 5);} catch (InterruptedException e) {e.printStackTrace();}// 5秒之后这里才会执行。System.out.println("hello World!");}
}class MyThread extends Thread {public void run() {for (int i = 0; i < 10000; i++) {System.out.println(Thread.currentThread().getName() + "--->" + i);}}
}
🍺输出结果(部分)

🍰分析
main方法中造了一个MyThread的对象,并且起了一个名字,调用了start方法。
紧接着有一个sleep方法,这个sleep方法是让主线程睡了还是让分线程睡了?
其实是主线程。
虽然t是一个线程,但是这里不能算是一个线程去调用sleep。只能理解为是一个对象去调用sleep。
t.sleep()代码的执行,是在主线程里面调用的。
静态方法,用类和对象调用都没有区别,都只会影响主线程。
(3)练习题3
🌋题目描述
银行有一个账户。
有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。
问题:该程序是否有安全问题,如果有,如何解决?
【提示】
1,明确哪些代码是多线程运行代码,须写入run()方法。
2,明确什么是共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
【拓展问题】可否实现两个储户交替存钱的操作
🍰分析
“同一个账户”就是共享数据。
如果有多线程,那就一定会有线程安全问题吗?
不一定。比如一个线程操作一个账户的钱,另一个线程操作另一个账户的钱,这就不会有线程安全问题。
但若是多个线程操作一个共享数据,就会有线程安全问题了。
本题目我们用继承Thread的方式来写。
比如现在有一个客户,我们让它继承于Thread类。如下:
public class AccountTest {}class Customer extends Thread{}
既然大家需要共用一个账户,那么这个账户如何体现共享?
用静态吗?
其实不用静态也可以!
比如现在声明一个Account类,就是账户类,里面有余额,如下:
class Account{ //账户private double balance; //余额
}
然后在客户Customer类里面声明一个Account属性,如下:
class Account{ //账户private double balance; //余额
}class Customer extends Thread{Account account;
}
这里就不将它写成静态的了,那能不能实现共享呢?
这就取决于它的构造器如何去使用了。
通过构造器给当前属性实例化一下,如下:
class Account{ //账户private double balance; //余额
}class Customer extends Thread{Account account;//构造器public Customer(Account acct){this.account=acct;}}
怎么保证造两个Customer,是同一个account呢?
只new一个对象,然后将值传进去即可。
比如在main方法中,造一个账户acct,然后new一个Customer的时候将acct传进去。
public class AccountTest {public static void main(String[] args) {Account acct=new Account();new Customer(acct);}
}
这样就可以创建两个Customer,如下:
public class AccountTest {public static void main(String[] args) {Account acct=new Account();Customer customer1=new Customer(acct);Customer customer2=new Customer(acct);}
}
现在这两个线程就共享同一个账户acct。
以后就可以使用这种思路来让线程共享资源啦,如下:
public class AccountTest {public static void main(String[] args) {Account acct=new Account();Customer customer1=new Customer(acct);Customer customer2=new Customer(acct);}
}class Account{ //账户private double balance; //余额
}class Customer extends Thread{Account account;//构造器public Customer(Account acct){this.account=acct;}}
同一个对象的实例变量是共享的。
在Thread类里面有一个可以起名字的构造器,这边就使用它来给线程起个名字吧。
来个重载构造器:
class Customer extends Thread{Account account;//构造器public Customer(Account acct,String name){super(name);this.account=acct;}}
现在就可以用这个构造器了,比如:
public class AccountTest {public static void main(String[] args) {Account acct=new Account();Customer customer1=new Customer(acct,"小旺");Customer customer2=new Customer(acct,"小岁");}
}
这样的话,就通过构造器的方式将线程的名字赋值好了。
现在两个储户需要存钱,需要在run方法里面做这个事情。
存三次,就循环三次:
class Customer extends Thread{//...@Overridepublic void run() {for (int i = 0; i < 3; i++) {}}
}
然后就是操作账户的余额,让余额balance三次增加。加钱的事情可以定义在Account类里面。如下:
class Account{ //账户private double balance; //余额public void deposit(double amt){if(amt>0){balance+=amt;}System.out.println(Thread.currentThread().getName()+"存钱1000元,余额为:"+balance);}
}
然后在刚才的for循环里面可以调用一下deposit方法。
class Customer extends Thread{//...@Overridepublic void run() {for (int i = 0; i < 3; i++) {account.deposit(1000);}}
}
当我们调用run方法的时候,就会进入for循环,然后调用account的deposit方法,首先往里面存了1000块钱。
然后各自线程去调用start方法,他们会各自调用run方法,去存钱。如下:
public class AccountTest {public static void main(String[] args) {Account acct=new Account();Customer customer1=new Customer(acct,"小旺");Customer customer2=new Customer(acct,"小岁");customer1.start();customer2.start();}
}
🌱代码
package yuyi03;/*** ClassName: AccountTest* Package: yuyi03* Description:** @Author 雨翼轻尘* @Create 2024/1/30 0030 14:56*/
public class AccountTest {public static void main(String[] args) {Account acct=new Account();Customer customer1=new Customer(acct,"小旺");Customer customer2=new Customer(acct,"小岁");customer1.start();customer2.start();}
}class Account{ //账户private double balance; //余额public void deposit(double amt){if(amt>0){balance+=amt;}System.out.println(Thread.currentThread().getName()+"存钱1000元,余额为:"+balance);}
}class Customer extends Thread{Account account;//构造器public Customer(Account acct){this.account=acct;}public Customer(Account acct,String name){super(name);this.account=acct;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {account.deposit(1000);}}
}
🍺输出结果

现在看着没有线程安全问题,接下来演示一下线程安全问题。
共享数据就是“钱数”balance。
在这里加一个sleep:

现在的代码:
public class AccountTest {public static void main(String[] args) {Account acct=new Account();Customer customer1=new Customer(acct,"小旺");Customer customer2=new Customer(acct,"小岁");customer1.start();customer2.start();}
}class Account{ //账户private double balance; //余额public void deposit(double amt){if(amt>0){balance+=amt;}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"存钱1000元,余额为:"+balance);}
}class Customer extends Thread{Account account;//构造器public Customer(Account acct){this.account=acct;}public Customer(Account acct,String name){super(name);this.account=acct;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {account.deposit(1000);}}
}
输出结果:

可以看到出现了问题。
这就是线程安全问题,怎么解决呢?
balance是共享数据,直接将操作写在deposit方法里面了,那么可以给这个方法直接加上synchronized吗?

能不能使用,取决于这个this是不是唯一的。
大家不要记“继承的方式this不唯一”,需要具体问题具体分析。
这个方法的调用者是Account类的对象,而Account类的对象只创建了一个,如下:

所以现在就是acct。既然是唯一的,那么线程就是安全的。
🌱代码
package yuyi03;/*** ClassName: AccountTest* Package: yuyi03* Description:** @Author 雨翼轻尘* @Create 2024/1/30 0030 14:56*/
public class AccountTest {public static void main(String[] args) {Account acct=new Account();Customer customer1=new Customer(acct,"小旺");Customer customer2=new Customer(acct,"小岁");customer1.start();customer2.start();}
}class Account{ //账户private double balance; //余额public synchronized void deposit(double amt){ //this:是唯一的,即为acttif(amt>0){balance+=amt;}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"存钱1000元,余额为:"+balance);}
}class Customer extends Thread{Account account;//构造器public Customer(Account acct){this.account=acct;}public Customer(Account acct,String name){super(name);this.account=acct;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {account.deposit(1000);}}
}
🍺输出结果

这就是之前说的,同步方法可以使用在“继承Thread类的子类”中的场景了,只要对象是同一个,那就可以直接使用非静态同步方法了。
🎲现在的结果显示小旺存完之后,小岁才存钱,要想体现交互可以让他睡一下,如下:

🌱代码
package yuyi03;/*** ClassName: AccountTest* Package: yuyi03* Description:** @Author 雨翼轻尘* @Create 2024/1/30 0030 14:56*/
public class AccountTest {public static void main(String[] args) {Account acct=new Account();Customer customer1=new Customer(acct,"小旺");Customer customer2=new Customer(acct,"小岁");customer1.start();customer2.start();}
}class Account{ //账户private double balance; //余额public synchronized void deposit(double amt){ //this:是唯一的,即为acttif(amt>0){balance+=amt;}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"存钱1000元,余额为:"+balance);}
}class Customer extends Thread{Account account;//构造器public Customer(Account acct){this.account=acct;}public Customer(Account acct,String name){super(name);this.account=acct;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}account.deposit(1000);}}
}
🍺输出结果

相关文章:
Java多线程--线程安全问题练习题
文章目录 (1)练习题1(2)练习题2(3)练习题3 现在咱们线程一共说了这么几件事情,如下: 具体文章见专栏。 接下来看几个练习题吧。 (1)练习题1 🌋题…...
PHY6252低成本Mesh组网蓝牙芯片
超低成本MESH组网蓝牙芯片PHY6252蓝牙Mesh组网简介 蓝⽛Mesh⽹络使⽤,依赖于低功耗蓝⽛(BLE)。低功耗蓝⽛技术是蓝⽛Mesh使用的无线通信协议栈,蓝牙BR/EDR能够与实现一台设备到另一台设备的连接和通信,建立“一对一”的关系,大多数…...
红外图像中两点校正的增益系数与偏置系数的计算公式推导
增益系数(K) 根据两个温度下的响应值,可求得各响应单元的响应曲线(即斜率),累加所有曲线的斜率求平均斜率值。 平均斜率值与各响应单元的斜率的比值即为该单元的K增益系数。结论:某单元的增益系…...
C++/MFC:在窗体Form(Dialog)中多个编辑框时,在输入时将回车解释为TAB键,将输入焦点移到下一个编辑框的方法
很多时候,为了输入方便,常用的做法,就是将回车键解释为将输入焦点移动到下一个编辑框中。就像是我的VxTerm中的快速连接输入一样: VxTerm是一个国产化替代的SSH工具,可以从本站的资源中免费下载并且免费使用ÿ…...
鸿蒙南向开发——GN快速入门指南
运行GN(Generate Ninja) 运行gn,你只需从命令行运行gn,对于大型项目,GN是与源码一起的。 对于Chromium和基于Chromium的项目,有一个在depot_tools中的脚本,它需要加入到你的PATH环境变量中。该脚本将在包含当前目录的…...
PyCharm常用快捷键和设置
Ctrl Space 基本的代码完成(类、方法、属性) Ctrl Alt Space 快速导入任意类 Ctrl Shift Enter 语句完成 Ctrl P 参数信息(在方法中调用参数) Ctrl Q 快速查看文档 F1 外部文档 Shift F1 外部文档…...
Unity - 调节camera物理相机参数(HDRP)
在 “Hierarchy” 右键 -> Volume -> Global Volume new 一个 profile, 设置Mode为Pysical Camera 再点击camera组件,这时候设置 ISO、Shutter Speed、Aperture等参数值还会有效。...
@JsonIgnore的使用及相关问题的解决
目录 1 前言 2 对比及其使用方法 3 遇到的相关问题及解决方法 1 前言 在我们编写的后端项目中,有时候可能需要将某个实体类以JSON格式传送给前端,但是其中可能有部分内容我们并不想传送,这时候我们选择将这部分内容变成Null,这…...
万户 ezOFFICE SendFileCheckTemplateEdit.jsp SQL注入漏洞
0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品,统一的基础管理平台,实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台,将外网信息维护、客户服务、互动交流和日…...
自建DNS劫持服务器,纯内网劫持PS5,屏蔽更新,自动hen
背景:目前PS5首次折腾必须要连外网,还要改DNS,除非使用ESP8266/32, 本文的方法是完全不改DNS,不使用ESP8266,不连接外网的情况下自动折腾 能实现什么: 1.折腾全程不连接外网 2.完全自建hen服务器ÿ…...
C语言王道第八周一题
Description 初始化顺序表(顺序表中元素为整型),里边的元素是 1,2,3,然后通过 scanf 读取一个元素(假如插入的是 6),插入到第 2 个位置,打印输出顺序表,每个 元素占 3 个…...
探索1688店铺所有商品API接口:一键获取海量数据,开启商业智能新篇章
1688店铺所有商品API接口技术详解 一、概述 1688店铺所有商品API接口是阿里巴巴提供的一套应用程序接口,允许第三方开发者获取指定1688店铺下的所有商品信息。通过使用这个接口,开发者可以获取到店铺内所有商品的列表、详情、属性等信息,从…...
使用Win32API实现贪吃蛇小游戏
目录 C语言贪吃蛇项目 基本功能 需要的基础内容 Win32API 介绍 控制台程序部分指令 设置控制台窗口的长宽 设置控制台的名字 控制台在屏幕上的坐标位置结构体COORD 检索指定标准设备的句柄(标准输入、标准输出或标准错误) 光标信息结构体类型CONSOLE_CUR…...
力扣0114——二叉树展开为链表
[二叉树展开为链表] 难度:中等 题目描述 给你二叉树的根结点 root ,请你将它展开为一个单链表: 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。展开后的单…...
FPGA硬件架构
1.Xilinx FPGA是异构计算平台(所谓异构,就是有很多不同的部分组成):CLB,BRAM,DSP 2. 软核: 把经过功能验证的、可综合的、实现后电路结构总门数在五千门以上的Verilog HDL模型称为软核(soft core)。 硬核: 把在某一…...
spring boot 嵌入chatGPT步骤
一、需要良好的网络 二、需要在OpenAI官网https://openai.com/注册用户,并获取一个api-key,sk开头的 验证是否可用网站:http://tools.lbbit.top/check_key_valid/ 三、spring boot 配置文件 openai.proxyHost127.0.0.1 openai.proxyPort7890…...
博云科技与中科可控全面合作,探索前沿金融科技新机遇
2024年1月26日,博云科技与中科可控在昆山高新区成功举办合作签约仪式。昆山市委常委、昆山高新区党工委书记孙道寻、中科可控董事长聂华、博云科技董事长花磊等领导出席了本次签约仪式。 中科可控将利用其在先进计算和智造领域的优势,为博云科技提供有关…...
十一、常用API——练习
常用API——练习 练习1 键盘录入:练习2 算法水题:练习3 算法水题:练习4 算法水题:练习5 算法水题: 练习1 键盘录入: 键盘录入一些1~100之间的整数,并添加到集合中。 直到集合中所有数据和超过2…...
基于ssm和微信小程序的健身房私教预约管理系统
文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式 🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 &…...
微服务架构
微服务(Microservices)是一种将应用程序作为独立服务套件的架构风格,这些服务围绕业务功能构建,可以通过网络调用进行交互。微服务架构使得可以独立开发、测试、部署和缩放各个服务。 微服务的核心原则包括: 高内聚&…...
可观测性最佳实践:构建全面的系统监控体系
可观测性最佳实践:构建全面的系统监控体系 一、可观测性最佳实践概述 1.1 可观测性的定义 可观测性是指通过外部输出(指标、日志、追踪)来推断系统内部状态的能力。它帮助运维人员理解系统行为,快速定位问题,优化系统性…...
小电视空降助手:告别B站广告烦恼的终极解决方案
小电视空降助手:告别B站广告烦恼的终极解决方案 【免费下载链接】BilibiliSponsorBlock 一款跳过小电视视频中恰饭片段的浏览器插件,移植自 SponsorBlock。A browser extension to skip sponsored segments in videos, ported from the SponsorBlock 项…...
Mac上高效调试HTTPS流量:Charles抓包配置与SSL解密实战
1. 为什么Mac用户绕不开Charles——它不是“又一个抓包工具”,而是调试链路的中枢神经在Mac上做前端联调、App接口验证、小程序网络行为分析,甚至排查第三方SDK异常请求时,我见过太多人卡在第一步:看不到真实发出去的请求。有人用…...
架构设计师 | 奠基之石:深入浅出,掌握系统工程五大方法论
一、引言1.1 系统工程核心定义系统工程是 20 世纪 40 年代伴随大型工程项目需求诞生的跨领域组织管理技术,是从整体视角对系统组成要素、组织结构、信息流、控制机制进行统筹分析的科学决策方法,核心目标是实现系统全生命周期的整体最优,而非…...
P1313 计算系数【洛谷算法习题】
P1313 计算系数 网页链接 P1313 计算系数 题目描述 给定一个多项式 (byax)k(byax)^k(byax)k,请求出多项式展开后 xnymx^n\times y^mxnym 项的系数。 输入格式 输入共一行,包含 555 个整数,分别为 a,b,k,n,ma,b,k,n,ma,b,k,n,m…...
每日热门skill:你的AI终于有“脑子“了!Memory MCP Server让Claude记住你的一切
告别"金鱼记忆",打造真正懂你的AI助手 一、开篇:那个让你崩溃的瞬间 你有没有遇到过这种情况? 昨天刚跟Claude说过:“我是做后端开发的,对Python比较熟悉,前端不太行。” 今天再问:“帮我写个React组件。” 它热情洋溢地回复:“好的!这是一个完整的全栈…...
【信息科学与工程学】计算机科学与自动化 ——第六十五篇 虚拟化/MIG 系列02
编号 类型 领域 虚拟化/MIG模式 算法名称 算法逐步推理思考的数学方程式及参数/常量/向量/常数/数字/数值列表 算法的时序数学方程式 关联知识 401 性能优化 GPU虚拟化+容器 MIG+容器 基于GPU内存带宽隔离的容器化AI训练任务调度算法 1. 带宽模型:每个MIG实例带宽…...
GORM 标签详解(数据库字段映射核心)
很多人刚学 GORM: 会觉得: gorm:"primaryKey" gorm:"index" gorm:"not null"这些东西: 像“魔法字符串”。 其实: 它本质上是在告诉 GORM: 数据库这一列应该怎么创建也就是:…...
告别账单惊吓,Taotoken Token Plan 如何让成本更可控
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 告别账单惊吓,Taotoken Token Plan 如何让成本更可控 对于依赖大模型 API 进行开发的团队和个人而言,项目成…...
DeepSeek免费额度怎么用才不浪费?资深MLOps工程师的6小时压测报告与最优请求批处理公式
更多请点击: https://kaifayun.com 第一章:DeepSeek免费额度怎么用才不浪费?资深MLOps工程师的6小时压测报告与最优请求批处理公式 在连续6小时、覆盖12种负载模式的真实压测中,我们发现DeepSeek API免费额度(当前为1…...
