文档目录:
一、概念
二、解决方案
三、举例说明
—————————————分割线:正文——————————————————–
一、概念
关注数据在多线程并发时安全问题,共享数据有修改的行为。
二、解决方案
1、线程排队执行,不能并发,即线程同步机制。
2、使用synchronized(){}线程同步代码块,()内填写需要同步的共享对象
3、局部变量永远不存在线程安全问题,因为局部变量是不会共享的
三、举例说明
1、编写程序模拟两个线程同时对同一个账户进行去取款操作
(1)变成账户类
1 package JAVAADVANCE;23 public class Account {4 //账户与余额5 private String actNo;6 private double balance;78 public Account(String actNo, double balance) {9 this.actNo = actNo;10 this.balance = balance;11 }1213 public String getActNo() {14 return actNo;15 }1617 public void setActNo(String actNo) {18 this.actNo = actNo;19 }2021 public double getBalance() {22 return balance;23 }2425 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 }3738 }
(2)编写账户线程类
1 package JAVAADVANCE;23 public class AccountThread extends Thread {4 //两个线程必须共享同一个账户对象5 private Account act;6 //通过构造方法传递过来的账户对象7 public AccountThread(Account act){8 this.act=act;9 }10 @Override11 public void run() {12 //run方法执行表示取款操作13 //假设取款500014 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;23 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 //设置name11 t1.setName(\"t1\");12 t2.setName(\"t2\");13 //启动线程取款14 t1.start();15 t2.start();16 }1718 }
(4)查看运行结果,一定几率出现钱被取完,余额未更新,出现线程安全问题
t2对账户act-001取款成功,余额为:5000.0t1对账户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 }1718 }
改进后再次执行,多线程不会并发
t1对账户act-001取款成功,余额为:5000.0t2对账户act-001取款成功,余额为:0.0
(2)whithdraw调用时候增加,synchronized ()线程同步代码块,扩大了同步范围,执行效率变低
1 @Override2 public void run() {3 //run方法执行表示取款操作4 //假设取款50005 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 }