Java多線程賣票例子


  以賣票的例子來介紹多線程和資源共享。

  賣票是包含一系列動作的過程,有各種操作,例如查詢票、收錢、數錢、出票等,其中有一個操作是每次賣掉一張,就將總的票數減去1。有10張票,如果一個人賣票,先做查票、收錢、數錢等各種操作,再將總的票數減去1,效率很低。如果多個人賣票,每個人都是做同樣的操作,數錢、檢查錢,最后將總的票數減1,這樣效率高。但是有一個問題,如果出現兩個人同時將總的票數減掉了1,例如,A、B兩個人同時讀取到票的總數是10,A從中減去1,同時B也從中減去1,總數顯示是9,其實票只有8張。導致數據錯誤。

  按照正常邏輯,同一時刻只允許一個人來從總票數中減去1,A讀取總票數,再減去1的過程中,B必須等待,等A操作完了,B才能進行。其實票就是共享資源,一次只能由一個人訪問。這里就要用到同步機制,即鎖機制,使用關鍵詞synchronized將讀取總的票數,並減去1的操作鎖定,使得一次只能由一個人訪問。每個售票員就是一個線程,多個售票員進行同一項賣票任務。

  synchronized原理是,執行synchronized部分代碼的時候必須需要對象鎖,而一個對象只有一個鎖,只有執行完synchronized里面的代碼后釋放鎖,其他線程才可以獲得鎖,那么就保證了同一時刻只有一個線程訪問synchronized里面的代碼。使得資源共享的關鍵是,只有一個實例,synchronized使用的是同一把鎖,用實例的鎖或者定義一個實例。這就需要使用實現Runnable接口的方式,實現多線程,這樣傳入的是一個實例。繼承Thread的方式,傳入的是多個實例,每個實例都有一個鎖,那就無法實現控制。

 1 package com.test;
 2 
 3 public class SaleTickets implements Runnable
 4 {
 5     private int ticketCount = 10;// 總的票數,這個是共享資源,多個線程都會訪問
 6     Object mutex = new Object();// 鎖,自己定義的,或者使用實例的鎖
 7 
 8     /**
 9      * 賣票
10      */
11     public void sellTicket()
12     {
13         synchronized (mutex)// 當操作的是共享數據時,
14                         // 用同步代碼塊進行包圍起來,執行里面的代碼需要mutex的鎖,但是mutex只有一個鎖。這樣在執行時,只能有一個線程執行同步代碼塊里面的內容
15         {
16             if (ticketCount > 0)
17             {
18                 ticketCount--;
19                 System.out.println(Thread.currentThread().getName()
20                         + "正在賣票,還剩" + ticketCount + "張票");
21             }
22             else
23             {
24                 System.out.println("票已經賣完!");
25                 return;
26             }
27         }
28     }
29 
30     public void run()
31     {
32         while (ticketCount > 0)// 循環是指線程不停的去賣票
33         {
34             sellTicket();
35             /**
36              * 在同步代碼塊里面睡覺,和不睡效果是一樣 的,作用只是自已不執行,也不讓線程執行。sleep不釋放鎖,抱着鎖睡覺。其他線程拿不到鎖,也不能執行同步代碼。wait()可以釋放鎖
37              * 所以把睡覺放到同步代碼塊的外面,這樣賣完一張票就睡一會,讓其他線程再賣,這樣所有的線程都可以賣票
38              */
39             try
40             {
41                 Thread.sleep(100);
42             }
43             catch (InterruptedException e)
44             {
45                 e.printStackTrace();
46             }
47         }
48     }
49 }

下面是調用

 1 package com.test;
 2 
 3 public class TicketMain
 4 {
 5 
 6     public static void main(String[] args)
 7     {
 8         SaleTickets runTicekt = new SaleTickets();//只定義了一個實例,這就只有一個Object mutex = new Object();即一個鎖。
 9         Thread th1 = new Thread(runTicekt, "窗口1");//每個線程等其他線程釋放該鎖后,才能執行
10         Thread th2 = new Thread(runTicekt, "窗口2");
11         Thread th3 = new Thread(runTicekt, "窗口3");
12         Thread th4 = new Thread(runTicekt, "窗口4");
13         th1.start();
14         th2.start();
15         th3.start();
16         th4.start();
17     }
18 
19 }

執行效果:

  這是多個線程,完成同一個任務的情況,即多個線程調用同一個實例,通過實現Runable接口實現。多個線程可以異步的做這個任務中其他事情,但是對於共享資源的訪問只能以同步的方式操作,即一個接一個訪問共享資源,其他資源可以並行訪問。

  另一種實現多線程的方式是繼承Thread,調用的時候需要傳遞多個實例,這是多個線程,多個實例的情況,每個線程獨立處理一個實例,各個線程不能實現資源共享。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM