java进阶(38)--线程安全

文档目录:

一、概念

二、解决方案

三、举例说明

---------------------------------------分割线:正文--------------------------------------------------------

一、概念

关注数据在多线程并发时安全问题,共享数据有修改的行为。

二、解决方案

1、线程排队执行,不能并发,即线程同步机制。

2、使用synchronized(){}线程同步代码块,()内填写需要同步的共享对象

3、局部变量永远不存在线程安全问题,因为局部变量是不会共享的

三、举例说明

1、编写程序模拟两个线程同时对同一个账户进行去取款操作

(1)变成账户类

 1 package JAVAADVANCE;
 2 
 3 public class Account {
 4     //账户与余额
 5     private String actNo;
 6     private double balance;
 7 
 8     public Account(String actNo, double balance) {
 9         this.actNo = actNo;
10         this.balance = balance;
11     }
12 
13     public String getActNo() {
14         return actNo;
15     }
16 
17     public void setActNo(String actNo) {
18         this.actNo = actNo;
19     }
20 
21     public double getBalance() {
22         return balance;
23     }
24 
25     public void setBalance(double balance) {
26         this.balance = balance;
27     }
28     //取款
29     public void withdraw(double money){
30         //取款之前的余额
31         double before=this.getBalance();
32         //取款之后的余额
33         double after = before-money;
34         //更新余额
35         this.setBalance(after);
36     }
37 
38 }

(2)编写账户线程类

 1 package JAVAADVANCE;
 2 
 3 public class AccountThread extends Thread {
 4     //两个线程必须共享同一个账户对象
 5     private Account act;
 6     //通过构造方法传递过来的账户对象
 7     public AccountThread(Account act){
 8         this.act=act;
 9     }
10     @Override
11     public void run() {
12         //run方法执行表示取款操作
13         //假设取款5000
14         double money=5000;
15         //取款
16         act.withdraw(money);
17         System.out.println(Thread.currentThread().getName()+"对账户"+act.getActNo()+"取款成功,余额为:"+act.getBalance());
18     }
19 }

(3)测试类进行取款操作

 1 package JAVAADVANCE;
 2 
 3 public class TestAdvance38TestThreadSafe01 {
 4     public static void main(String[] args) {
 5         //创建账户对象,只创建一个
 6         Account act=new Account("act-001",10000);
 7         //创建两个线程
 8         Thread t1=new AccountThread(act);
 9         Thread t2=new AccountThread(act);
10         //设置name
11         t1.setName("t1");
12         t2.setName("t2");
13         //启动线程取款
14         t1.start();
15         t2.start();
16     }
17 
18 }

(4)查看运行结果,一定几率出现钱被取完,余额未更新,出现线程安全问题

t2对账户act-001取款成功,余额为:5000.0
t1对账户act-001取款成功,余额为:5000.0

2、改进代码,使用线程同步机制来解决以上问题

 (1)加入synchronized ()线程同步代码块

()内需要填写多线程共享的数据,才能达到多线程排队。

 1     //取款
 2     public void withdraw(double money){
 3         //增加线程同步机制,里面是线程同步代码块
 4         synchronized (this){
 5             //取款之前的余额
 6             double before=this.getBalance();
 7             //取款之后的余额
 8             double after = before-money;
 9             try {
10                 Thread.sleep(1000);
11             } catch (InterruptedException e) {
12                 e.printStackTrace();
13             }
14             //更新余额
15             this.setBalance(after);
16         }
17 
18     }

改进后再次执行,多线程不会并发

t1对账户act-001取款成功,余额为:5000.0
t2对账户act-001取款成功,余额为:0.0

(2)whithdraw调用时候增加,synchronized ()线程同步代码块,扩大了同步范围,执行效率变低

 1     @Override
 2     public void run() {
 3         //run方法执行表示取款操作
 4         //假设取款5000
 5         double money=5000;
 6         //取款
 7         synchronized (act){
 8         act.withdraw(money);}
 9         System.out.println(Thread.currentThread().getName()+"对账户"+act.getActNo()+"取款成功,余额为:"+act.getBalance());
10     }

执行效果同上:多线程不会并发

(3)将实例方法使用synchronized

缺点:一定需要锁this,整个方法体都需要同步,可能会扩大同步范围导致程序效率过低

有点:代码比较少,简介

 1     //取款
 2     public synchronized void withdraw(double money){
 3         //增加线程同步机制,里面是线程同步代码块
 4 //        synchronized (this){
 5             //取款之前的余额
 6             double before=this.getBalance();
 7             //取款之后的余额
 8             double after = before-money;
 9             try {
10                 Thread.sleep(1000);
11             } catch (InterruptedException e) {
12                 e.printStackTrace();
13             }
14             //更新余额
15             this.setBalance(after);
16         }

 

posted @ 2021-04-02 23:55  Mrwhite86  阅读(239)  评论(0编辑  收藏  举报