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 }