本文樓主主要以用戶在售票廳購買車票爲背景進行多線程的實現。假設A市到B市的車票共50張,共有3個售票窗口在進行售票,使用多線程來模擬理想狀況下的用戶購票:git
實現Runnable的Ticket類:github
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張車票徹底售出:網絡
1 售票窗口2正在售第50張車票 2 售票窗口3正在售第49張車票 3 售票窗口1正在售第48張車票 4 售票窗口3正在售第46張車票 5 售票窗口2正在售第47張車票 6 售票窗口3正在售第44張車票 7 售票窗口1正在售第45張車票 8 售票窗口3正在售第42張車票 9 售票窗口2正在售第43張車票 10 售票窗口3正在售第40張車票 11 售票窗口1正在售第41張車票 12 售票窗口3正在售第38張車票 13 售票窗口2正在售第39張車票 14 售票窗口2正在售第35張車票 15 售票窗口3正在售第36張車票 16 售票窗口3正在售第33張車票 17 售票窗口3正在售第32張車票 18 售票窗口3正在售第31張車票 19 售票窗口3正在售第30張車票 20 售票窗口3正在售第29張車票 21 售票窗口3正在售第28張車票 22 售票窗口1正在售第37張車票 23 售票窗口3正在售第27張車票 24 售票窗口2正在售第34張車票 25 售票窗口3正在售第25張車票 26 售票窗口1正在售第26張車票 27 售票窗口1正在售第22張車票 28 售票窗口1正在售第21張車票 29 售票窗口1正在售第20張車票 30 售票窗口1正在售第19張車票 31 售票窗口1正在售第18張車票 32 售票窗口1正在售第17張車票 33 售票窗口1正在售第16張車票 34 售票窗口1正在售第15張車票 35 售票窗口1正在售第14張車票 36 售票窗口1正在售第13張車票 37 售票窗口1正在售第12張車票 38 售票窗口1正在售第11張車票 39 售票窗口1正在售第10張車票 40 售票窗口1正在售第9張車票 41 售票窗口1正在售第8張車票 42 售票窗口1正在售第7張車票 43 售票窗口1正在售第6張車票 44 售票窗口1正在售第5張車票 45 售票窗口1正在售第4張車票 46 售票窗口1正在售第3張車票 47 售票窗口1正在售第2張車票 48 售票窗口1正在售第1張車票 49 售票窗口3正在售第23張車票 50 售票窗口2正在售第24張車票
可是在實際應用場景中,咱們一般要考慮到由於網絡延遲等其餘因素形成的購票延遲,這裏咱們將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 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 ,很明顯出現了線程安全問題:ide
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號
產生這種結果的緣由:測試
假設系統在出售「第44張車票」的時候,線程「售票窗口1」獲取到了CPU的執行權,流程用下圖表示:spa
判斷應用程序是否有線程安全的問題不外乎如下幾點: 線程
很明顯上面的程序都知足這三點,解決思路:把多個語句操做共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行便可。樓主這裏使用同步代碼塊改造Ticket類以下:3d
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 }
再來運行,結果以下:
1 售票窗口3正在售第50張車票 2 售票窗口3正在售第49張車票 3 售票窗口3正在售第48張車票 4 售票窗口1正在售第47張車票 5 售票窗口1正在售第46張車票 6 售票窗口1正在售第45張車票 7 售票窗口2正在售第44張車票 8 售票窗口2正在售第43張車票 9 售票窗口2正在售第42張車票 10 售票窗口2正在售第41張車票 11 售票窗口1正在售第40張車票 12 售票窗口1正在售第39張車票 13 售票窗口3正在售第38張車票 14 售票窗口1正在售第37張車票 15 售票窗口2正在售第36張車票 16 售票窗口2正在售第35張車票 17 售票窗口2正在售第34張車票 18 售票窗口2正在售第33張車票 19 售票窗口2正在售第32張車票 20 售票窗口2正在售第31張車票 21 售票窗口1正在售第30張車票 22 售票窗口3正在售第29張車票 23 售票窗口3正在售第28張車票 24 售票窗口3正在售第27張車票 25 售票窗口3正在售第26張車票 26 售票窗口3正在售第25張車票 27 售票窗口3正在售第24張車票 28 售票窗口3正在售第23張車票 29 售票窗口3正在售第22張車票 30 售票窗口3正在售第21張車票 31 售票窗口1正在售第20張車票 32 售票窗口1正在售第19張車票 33 售票窗口1正在售第18張車票 34 售票窗口2正在售第17張車票 35 售票窗口2正在售第16張車票 36 售票窗口2正在售第15張車票 37 售票窗口1正在售第14張車票 38 售票窗口3正在售第13張車票 39 售票窗口3正在售第12張車票 40 售票窗口3正在售第11張車票 41 售票窗口1正在售第10張車票 42 售票窗口1正在售第9張車票 43 售票窗口2正在售第8張車票 44 售票窗口2正在售第7張車票 45 售票窗口2正在售第6張車票 46 售票窗口1正在售第5張車票 47 售票窗口1正在售第4張車票 48 售票窗口1正在售第3張車票 49 售票窗口3正在售第2張車票 50 售票窗口3正在售第1張車票
能夠看到,再也不有重複的票出現。固然同步代碼塊也有它的弊端,當線程至關多時,由於每一個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會下降程序的運行效率。