文件目錄:
一、概念
二、解決方案
三、舉例說明
---------------------------------------分割線:正文--------------------------------------------------------
一、概念
關注資料在多執行緒併發時安全問題,共享資料有修改的行為。
二、解決方案
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 }