Java线程安全和同步
2026/6/5 9:21:42 网站建设 项目流程

目录

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有深度优化

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询