JAVA學習之路(多線程)---模擬售票(細解)


首先看題目描述:

假設有火車票100張,創建4個線程模擬4個售票點,每100ms售出一張,打印出售票過程,格式如下:

窗口3:賣出第100張票

窗口4:賣出第99張票 

............

............

簡單的思路就是創建一個類,首先肯定要去繼承Thread。開啟線程,由於是4個窗口,肯定要開啟4個線程。然后讓每個線程去輸出結果,也就是賣出去的票。這里很多人想不到如何讓4個線程不打印重復的票。(比如4個線程都賣出去了第100張票,這顯然是不合理的)。

看代碼:

  

 1 package com.lesson.thread;
 2 
 3 public class MyThread {
 4 
 5     public static void main(String[] args) {
 6         Ticket sell1 = new Ticket();
 7         Ticket sell2 = new Ticket();
 8         Ticket sell3 = new Ticket();
 9         Ticket sell4 = new Ticket();
10         sell1.setName("窗口1");
11         sell2.setName("窗口2");
12         sell3.setName("窗口3");
13         sell4.setName("窗口4");
14         sell1.start();
15         sell2.start();
16         sell3.start();
17         sell4.start();
18     }
19 }
20 class Ticket extends Thread {
21     private static int tickets = 100;//這里設置成static,目的是讓每個線程共享這個變量。以免出現重復打印的現象。
22     @Override
23     public void run() {
24         while(true) {
25             if(tickets <= 0) {
26                 break;
27             }
2829          System.out.println(getName()+":買出第"+tickets--+"張票。");//賣出一張減一張票
30 31         }
32     }
33     
34 }

可能你已經看到了你想要的結果了。但是,還沒完。目前代碼寫到這里是有問題的!!!

為什么?

看下面的代碼:

package thread;
public class Mythread {

    public static void main(String[] args) {
        Ticket sell1 = new Ticket();
        Ticket sell2 = new Ticket();
        Ticket sell3 = new Ticket();
        Ticket sell4 = new Ticket();
        sell1.setName("窗口1");
        sell2.setName("窗口2");
        sell3.setName("窗口3");
        sell4.setName("窗口4");
        sell1.start();
        sell2.start();
        sell3.start();
        sell4.start();
    }
}
class Ticket extends Thread {
    private static int tickets = 100;
    @Override
    public void run() {
        while(true) {
            if(tickets <= 0) {
                break;
            }
            try {
                Thread.sleep(10);          //讓進來的線程睡10ms;線程1,2,3,4都睡在這
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(getName()+":買出了第"+tickets--+"張票。");
        }
    }    
}

與開始的代碼不同在於讓進來的線程睡一會。可以看到如下運行結果:

窗口2:買出了第0張票。
窗口3:買出了第-1張票。
窗口1:買出了第-2張票

這里編者調出來了出問題的地方。可以看到怎么會第0,-1,-2張票???

做出解釋:

其實在這里讓線程睡一會就是為了演示這里有很多行代碼要執行。假設票已經賣到第1張了,也就是tickets=1,然后第一條線程進來判斷 tickets <= 0 ?不成立,然后線程1要睡 10ms,緊接着,線程2進來,這時 tickets 還是為1,因為線程1在睡,tickets 沒有減 。然后線程2判斷 tickets <= 0 ? 還是不成立,線程2又開始睡。同樣,線程3,線程4都睡了。 這時的tickets 還是等於1的。然后線程1先醒過來,開始輸出結果,tickets 減了1。可是這是其他的線程還是經過while里面的判斷語句進來了的,只是睡了。所以當其他線程醒過來的時候,還是會打印出結果的。也就出現了上面的問題。

解決方法:

 多線程並發改變同一變量,為了解決,采用同步代碼塊synchronized。里面加任意的對象,但是不能加this,因為這里創建了四個線程,每一個線程都有自己的對象,所以是四個不同的對象,沒有用。所以這里不能用this,必須鎖在同一個對象里才行。而Thickets.class這是唯一的。

package thread;
public class Mythread {

    public static void main(String[] args) {
        Ticket sell1 = new Ticket();
        Ticket sell2 = new Ticket();
        Ticket sell3 = new Ticket();
        Ticket sell4 = new Ticket();
        sell1.setName("窗口1");
        sell2.setName("窗口2");
        sell3.setName("窗口3");
        sell4.setName("窗口4");
        sell1.start();
        sell2.start();
        sell3.start();
        sell4.start();
    }
}
class Ticket extends Thread {
    private static int tickets = 100;
    @Override
    public void run() {
        while(true) {
            synchronized (Ticket.class) {//同步代碼塊
                if(tickets <= 0) {
                    break;
                }
                try {
                    Thread.sleep(100);          //每100ms賣出一張
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName()+":買出了第"+tickets--+"張票。");
            }
        }
    }    
}

 


免責聲明!

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



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