線程安全問題

本文樓主主要以用戶在售票廳購買車票爲背景進行多線程的實現。假設A市到B市的車票共50張,共有3個售票窗口在進行售票,使用多線程來模擬理想狀況下的用戶購票:安全

實現Runnable的Ticket類:網絡

複製代碼

 1 package com.jon.thread; 2  3 public class TicketSell implements Runnable { 4     private int tickets = 50;//設置車票數量 5     @Override 6     public void run() { 7         while(true){ 8             if(tickets>0){        
 9                 //輸出當前是哪一個線程在出售第幾張車票10                 System.out.println(Thread.currentThread().getName() + "正在售第" + (tickets--) + "張車票");11             }12         }13     }14 15 }

複製代碼

簡單的售票業務構建好後,咱們用三個線程模擬售票窗口來進行測試:多線程

複製代碼

 1 package com.jon.thread; 2  3 public class TicketSellTest { 4     public static void main(String[] args) { 5         TicketSell ts = new TicketSell(); 6         Thread td1 = new Thread(ts, "售票窗口1");//設置線程名稱以區分哪一個售票窗口 7         Thread td2 = new Thread(ts, "售票窗口2"); 8         Thread td3 = new Thread(ts, "售票窗口3"); 9         td1.start();10         td2.start();11         td3.start();12     }13 }

複製代碼

輸出結果能夠看到,三個線程搶佔式地將50張車票徹底售出:ide

 View Code測試

可是在實際應用場景中,咱們一般要考慮到由於網絡延遲等其餘因素形成的購票延遲,這裏咱們將Ticket稍微進行了改造:spa

複製代碼

 1 package com.jon.thread; 2  3 public class TicketSell implements Runnable { 4     private int tickets = 50;//設置車票數量 5     @Override 6     public void run() { 7         while(true){ 8             try { 9                 Thread.sleep(100);//將線程睡眠100毫秒用來模擬延遲10             } catch (InterruptedException e) {                
11                 e.printStackTrace();12             }13             if(tickets>0){        
14                 //輸出當前是哪一個線程在出售第幾張車票15                 System.out.println(Thread.currentThread().getName() + "正在售第" + (tickets--) + "張車票");16             }17         }18     }19 20 }

複製代碼

再次運行,能夠看到有些售票窗口售出了相同的票,甚至還出現了-一、0 ,很明顯出現了線程安全問題:線程

複製代碼

 1 售票窗口1正在售第49張車票 2 售票窗口2正在售第49張車票 3 售票窗口3正在售第50張車票 4 售票窗口2正在售第48張車票 5 售票窗口1正在售第46張車票 6 售票窗口3正在售第47張車票 7 售票窗口2正在售第45張車票 8 售票窗口1正在售第44張車票//窗口1,3出售了相同的44號車票 9 售票窗口3正在售第44張車票10 售票窗口2正在售第43張車票11 售票窗口1正在售第41張車票12 售票窗口3正在售第42張車票13 售票窗口2正在售第40張車票14 售票窗口3正在售第39張車票15 售票窗口1正在售第39張車票16 售票窗口1正在售第38張車票17 售票窗口2正在售第37張車票18 售票窗口3正在售第36張車票19 售票窗口1正在售第35張車票20 售票窗口3正在售第33張車票21 售票窗口2正在售第34張車票22 售票窗口1正在售第32張車票23 售票窗口3正在售第31張車票24 售票窗口2正在售第30張車票25 售票窗口3正在售第29張車票26 售票窗口1正在售第29張車票27 售票窗口2正在售第28張車票28 售票窗口3正在售第27張車票29 售票窗口1正在售第27張車票30 售票窗口2正在售第26張車票31 售票窗口1正在售第25張車票32 售票窗口3正在售第24張車票33 售票窗口2正在售第23張車票34 售票窗口1正在售第22張車票35 售票窗口3正在售第21張車票36 售票窗口2正在售第20張車票37 售票窗口1正在售第19張車票38 售票窗口3正在售第18張車票39 售票窗口2正在售第17張車票40 售票窗口3正在售第16張車票41 售票窗口1正在售第15張車票42 售票窗口2正在售第14張車票43 售票窗口3正在售第13張車票44 售票窗口1正在售第12張車票45 售票窗口2正在售第11張車票46 售票窗口1正在售第10張車票47 售票窗口3正在售第9張車票48 售票窗口2正在售第8張車票49 售票窗口1正在售第7張車票50 售票窗口3正在售第6張車票51 售票窗口2正在售第5張車票52 售票窗口1正在售第4張車票53 售票窗口2正在售第2張車票54 售票窗口3正在售第3張車票55 售票窗口1正在售第0張車票56 售票窗口3正在售第1張車票57 售票窗口2正在售第-1張車票//甚至出現了-1號、0號

複製代碼

產生這種結果的緣由:3d

  假設系統在出售「第44張車票」的時候,線程「售票窗口1」獲取到了CPU的執行權,流程用下圖表示:code

判斷應用程序是否有線程安全的問題不外乎如下幾點:  orm

*是不是多線程環境

*是否有共享數據

*是否有多條語句操做共享數據

很明顯上面的程序都知足這三點,解決思路:把多個語句操做共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行便可。樓主這裏使用同步代碼塊改造Ticket類以下:

複製代碼

 1 package com.jon.thread; 2  3 public class TicketSell implements Runnable { 4     private int tickets = 50; 5     private Object obj = new Object(); 6     @Override 7     public void run() { 8         while(true){ 9             synchronized (obj) {10                 try {11                     Thread.sleep(100);12                 } catch (InterruptedException e) {                
13                     e.printStackTrace();14                 }15                 if(tickets>0){                
16                     System.out.println(Thread.currentThread().getName() + "正在售第" + (tickets--) + "張車票");17                 }18             }            
19         }20     }21 22 }

複製代碼

再來運行,結果以下:

 View Code

能夠看到,再也不有重複的票出現。固然同步代碼塊也有它的弊端,當線程至關多時,由於每一個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會下降程序的運行效率。

相關文章
相關標籤/搜索