news 2026/4/17 18:12:29

Java线程安全和同步

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java线程安全和同步

目录

1.场景

2.编写程序

3.解决办法:线程同步

1)synchronized同步代码块

2)synchronized同步方法

3)两个方式的对比

4)写一个锁对象

4.使用场景

使用Lock的场景

使用synchronized的场景


1.场景

小明和小红是一对夫妻,他们有一个共同的账户,有余额十万块钱,有一天他们各自同时取了十万块钱。那谁能成功取到这十万呢?

2.编写程序

1)创建账户类,创建账户对象来代表夫妻的共同账户,提供取钱方法

package ThreadSecurity; public class Account { private Integer money; public Account() {} public Account(Integer money) { this.money = money; } public Integer getMoney() { return money; } public void setMoney(Integer money) { this.money = money; } //取钱方法 public void drawMoney(Integer money) { //看看是谁来取钱 String name = Thread.currentThread().getName(); //验证余额 if (this.money >= money) { //先打印一次,以免程序执行过快反而不出现安全问题 System.out.println(name+"取钱成功,取出了"+money+"元"); this.money -= money ; System.out.println(name + "取钱成功,余额" + this.money + "元"); } else { System.out.println("余额不足"); } } }

2)使用线程类,运行两个线程来模拟取钱过程

package ThreadSecurity; public class MyThread implements Runnable{ private Account account; public MyThread(Account account) { this.account = account; } @Override public void run() { account.drawMoney(100000); } }

3)测试

package ThreadSecurity; /** * 模拟线程同步问题 * 小明和小红同时取同一个账户的钱 */ public class Test { public static void main(String[] args) { //创建账户类 Account account = new Account(100000); //创建小明线程 Thread xm=new Thread(new MyThread(account)); xm.setName("小明"); xm.start(); //创建小红线程 Thread xh=new Thread(new MyThread(account)); xh.setName("小红"); xh.start(); } }

结果:(也有可能是小红取钱成功)

可见,如果两个人都取钱成功,那么银行就会亏损十万元。

那么该怎么解决呢?

3.解决办法:线程同步

1)synchronized同步代码块

作用:把访问共享资源的核心代码块上锁,以此保证线程安全

在核心逻辑取钱方法当中修改:

package ThreadSynchronized01; public class Account { private Integer money; public Account() {} public Account(Integer money) { this.money = money; } public Integer getMoney() { return money; } public void setMoney(Integer money) { this.money = money; } //取钱方法 public void drawMoney(Integer money) { //看看是谁来取钱 String name = Thread.currentThread().getName(); //synchronized代码块:给代码块上锁 //线程通过竞争锁,拿到锁对象(this)之后可以执行代码块,执行完之后释放 synchronized (this) { //验证余额 if (this.money >= money) { //先打印一次,以免程序执行过快反而不出现安全问题 System.out.println(name+"取钱成功,取出了"+money+"元"); this.money -= money ; System.out.println(name + "取钱成功,余额" + this.money + "元"); } else { System.out.println(name+"取钱失败,余额不足"); } } } }

语法

synchronized(锁对象){

//业务逻辑......

}

锁对象可以是其他对象,但是不建议,因为如果是某一常量的话,可能会影响其他无关线程的运行。用this合适,因为这样不会影响其他无关线程的执行,也能起到锁的作用。

对于实例方法,建议使用this作为锁对象

对于静态方法,建议使用字节码(类名.class)作为锁对象

执行结果:(结果不唯一)

2)synchronized同步方法

作用:把访问共享资源的核心方法上锁,以此保证线程安全

修改取钱方法:

package ThreadSynchronized02; public class Account { private Integer money; public Account() {} public Account(Integer money) { this.money = money; } public Integer getMoney() { return money; } public void setMoney(Integer money) { this.money = money; } //取钱方法 //synchronized同步方法,和synchronized代码块大致相同 //synchronized方法的性能比synchronized代码块低,因为synchronized代码块可以让其他线程先处理没有线程安全的代码 //synchronized同步方法维护了一个隐式锁 public synchronized void drawMoney(Integer money) { //看看是谁来取钱 String name = Thread.currentThread().getName(); //验证余额 if (this.money >= money) { //先打印一次,以免程序执行过快反而不出现安全问题 System.out.println(name+"取钱成功,取出了"+money+"元"); this.money -= money ; System.out.println(name + "取钱成功,余额" + this.money + "元"); } else { System.out.println(name+"取钱失败,余额不足"); } } }

结果:(结果不唯一)

3)两个方式的对比

4)写一个锁对象

Lock是Java并发包java.util.concurrent.locks中提供的显式锁机制,相比synchronized内置锁,它提供了更灵活、功能更丰富的锁控制

以ReentrantLock(可重入锁)为例:

package ThreadLock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Account { //锁对象,加上final防止锁被修改 private final Lock lock = new ReentrantLock(); private Integer money; public Account() {} public Account(Integer money) { this.money = money; } public Integer getMoney() { return money; } public void setMoney(Integer money) { this.money = money; } //取钱方法 public void drawMoney(Integer money) { //看看是谁来取钱 String name = Thread.currentThread().getName(); //ctrl+alt+t选中代码块 try { //上锁 lock.lock(); //验证余额 if (this.money >= money) { //先打印一次,以免程序执行过快反而不出现安全问题 System.out.println(name+"取钱成功,取出了"+money+"元"); this.money -= money ; System.out.println(name + "取钱成功,余额" + this.money + "元"); } else { System.out.println("余额不足"); } } catch (Exception e) { throw new RuntimeException(e); }finally { //unlock必须执行,如果前面发生异常,也要解锁,所以要在finally里面 lock.unlock(); } } }

结果(不唯一):

4.使用场景

使用Lock的场景

需要高级功能:可中断、超时、尝试锁

需要多个条件变量:复杂的线程协调

读写分离场景:读多写少

需要公平性控制:按顺序获取锁

性能关键场景:高竞争下的性能优化

使用synchronized的场景

简单同步需求:基本的互斥访问

代码简洁性:减少代码复杂度

自动管理:避免忘记释放锁

内建优化:JVM对synchronized有深度优化

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 0:05:49

揭秘AZ-500云Agent监控盲区:90%运维人员忽略的3个关键指标

第一章:AZ-500云Agent监控的核心价值在现代混合云架构中,Azure Monitor Agent(AZ-500)作为核心监控组件,承担着数据采集、安全合规与性能洞察的关键职责。通过统一的数据收集管道,AZ-500能够从虚拟机、容器…

作者头像 李华
网站建设 2026/4/15 21:57:58

Kotaemon能否用于天气预报问答?时效性信息挑战

Kotaemon能否用于天气预报问答?时效性信息挑战 在智能客服、金融分析甚至医疗咨询中,大语言模型(LLM)已经展现出惊人的自然语言理解能力。但当我们问出“今天北京会下雨吗?”这样的问题时,大多数AI系统就开…

作者头像 李华
网站建设 2026/4/12 0:42:14

23、多线程同步问题的排查与解决

多线程同步问题的排查与解决 在多线程编程中,同步问题是一个常见且棘手的挑战。本文将详细介绍解决同步问题的一般流程,以及常见的同步问题场景和相应的解决方案。 1. 解决同步问题的一般流程 解决代码中的同步问题可以遵循以下步骤: graph LRA[应用是否出现同步症状?…

作者头像 李华
网站建设 2026/4/18 0:56:43

32、Windows Vista 调试与同步特性深度解析

Windows Vista 调试与同步特性深度解析 1. 进程间通信变化 Windows Vista 在进程间通信方面的改变主要局限于单个物理系统内部。在异构网络中运行时,其网络可观察行为与之前的操作系统相似,基于网络流量解析的技术仍然适用。不过,同一物理系统内各组件间的通信模型发生了变…

作者头像 李华
网站建设 2026/4/17 16:27:47

量子计算任务调度难题:如何用智能Agent实现毫秒级响应?

第一章:量子计算任务调度的挑战与机遇随着量子计算从理论研究逐步迈向实际应用,如何高效调度量子任务成为制约系统性能的关键瓶颈。传统经典计算中的任务调度策略难以直接迁移至量子环境,主要受限于量子比特的脆弱性、门操作的时序依赖以及硬…

作者头像 李华
网站建设 2026/4/18 5:38:34

工业机器人多Agent系统设计指南(稀缺架构方案首次公开)

第一章:工业机器人Agent的协作在现代智能制造系统中,工业机器人不再作为孤立单元运行,而是以智能Agent的形式参与协同作业。每个机器人Agent具备感知、决策与通信能力,能够在动态环境中与其他Agent协调任务分配、路径规划与资源调…

作者头像 李华