news 2026/6/15 0:29:12

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/6/15 4:55:57

27、深入探究Windows错误调试与报告机制

深入探究Windows错误调试与报告机制 在软件开发和系统运维过程中,错误调试与报告是解决问题、提升软件质量的关键环节。本文将详细介绍Windows系统中利用转储文件进行事后调试以及Windows错误报告服务的相关内容。 1. 转储文件的使用 转储文件是进程状态的静态快照,可用于…

作者头像 李华
网站建设 2026/6/15 10:23:21

28、软件调试中的错误报告与分析工具

软件调试中的错误报告与分析工具 1. Windows Error Reporting 与 Corporate Error Reporting 1.1 Windows Error Reporting 概述 Windows Error Reporting(WER)是一项强大的服务,它能让开发者监控应用程序在实际环境中的运行状况。当用户遇到与响应关联的故障时,会看到特…

作者头像 李华
网站建设 2026/6/15 10:40:32

31、Windows Vista 安全特性深度解析(上)

Windows Vista 安全特性深度解析(上) 1. 堆管理器的革新 Windows Vista 的堆管理器经历了重大改进。之前的元数据显示一个子段包含 15 个大小为 0x208 字节的块,随后是该子段中的堆块列表,每个块都标记有 LFH,表明是低碎片堆块。新的低碎片前端分配器极大地改善了与安全…

作者头像 李华
网站建设 2026/6/14 22:30:49

33、内存堆与锁的测试设置详解

内存堆与锁的测试设置详解 在软件开发过程中,内存堆(Heaps)和锁(Locks)相关的问题常常困扰着开发者。下面将详细介绍内存堆和锁的测试设置,帮助开发者更好地调试和解决相关问题。 1. 内存堆测试设置 内存堆测试设置包含强大的工具,能帮助开发者更轻松地解决与堆相关的…

作者头像 李华
网站建设 2026/6/15 10:37:28

【MCP续证时间红线预警】:超期1天=重新考试?真相令人震惊

第一章:MCP 续证的时间限制 Microsoft Certified Professional(MCP)认证作为IT从业者技术能力的重要凭证,其有效期与续证时间限制直接影响职业发展路径。微软近年来对认证体系进行了调整,多数认证不再设置传统的“永久…

作者头像 李华