1. 程式人生 > >11、多執行緒視窗銷售火車票之初體驗

11、多執行緒視窗銷售火車票之初體驗

前面的博文中記錄了一些關於多執行緒比較基礎的一些API知識,本篇博文將在此基礎上,整一個簡單的模擬火車票銷售的場景。此文只是簡單的模擬,後續會對此進行改進!

package com.demo10;

public class Tickets extends Thread{
    private int lastTickets = 50;

    @Override
    public void run() {
        while (true) {
            if(lastTickets > 0){
                lastTickets--;
                System.out.println(Thread.currentThread().getName() + "賣出一張,餘票:" + lastTickets + "張");
            }else{
                System.out.println("票已經賣完了!");
                Thread.currentThread().stop();
            }
        }
    }

}
package com.demo10;

public class Run {
    public static void main(String[] args) {
        Tickets tickets = new Tickets();
        Thread t1 = new Thread(tickets,"視窗A");
        Thread t2 = new Thread(tickets,"視窗B");
        Thread t3 = new Thread(tickets,"視窗C");
        t1.start();
        t2.start();
        t3.start();
    }
}

輸出結果:

視窗A賣出一張,餘票:49張
視窗A賣出一張,餘票:48張
視窗A賣出一張,餘票:47張
視窗A賣出一張,餘票:46張
視窗A賣出一張,餘票:45張
視窗A賣出一張,餘票:44張
視窗A賣出一張,餘票:43張
視窗A賣出一張,餘票:42張
視窗A賣出一張,餘票:41張
視窗A賣出一張,餘票:40張
視窗A賣出一張,餘票:39張
視窗A賣出一張,餘票:38張
視窗A賣出一張,餘票:37張
視窗A賣出一張,餘票:36張
視窗A賣出一張,餘票:35張
視窗A賣出一張,餘票:34張
視窗B賣出一張,餘票:33張
視窗A賣出一張,餘票:32張
視窗B賣出一張,餘票:31張
視窗A賣出一張,餘票:30張
視窗B賣出一張,餘票:29張
視窗A賣出一張,餘票:27張
視窗C賣出一張,餘票:27張
視窗A賣出一張,餘票:25張
視窗B賣出一張,餘票:26張
視窗A賣出一張,餘票:23張
視窗C賣出一張,餘票:24張
視窗A賣出一張,餘票:21張
視窗B賣出一張,餘票:22張
視窗A賣出一張,餘票:19張
視窗C賣出一張,餘票:20張
視窗A賣出一張,餘票:17張
視窗B賣出一張,餘票:18張
視窗A賣出一張,餘票:15張
視窗C賣出一張,餘票:16張
視窗A賣出一張,餘票:13張
視窗B賣出一張,餘票:14張
視窗A賣出一張,餘票:11張
視窗C賣出一張,餘票:12張
視窗A賣出一張,餘票:9張
視窗B賣出一張,餘票:10張
視窗A賣出一張,餘票:7張
視窗C賣出一張,餘票:8張
視窗A賣出一張,餘票:5張
視窗B賣出一張,餘票:6張
視窗A賣出一張,餘票:3張
視窗C賣出一張,餘票:4張
視窗A賣出一張,餘票:1張
視窗B賣出一張,餘票:2張
票已經賣完了!
票已經賣完了!
視窗C賣出一張,餘票:0張
票已經賣完了!

從輸出結果來看啊,三個賣票視窗(執行緒)啟動之後,都在各賣各的,也不知道自己賣的票號是不是已經被其他視窗賣出去了,產生了多個視窗賣同一張票的錯誤。這是對消費者的一種欺騙啊。為了防止類似的事情發生,我們必須採取強烈的措施來對賣票視窗進行管理。目前採用的是加同步鎖。意思就是 每一張票(資源)都是公平的擺在那裡供三個視窗銷售,但是隻要某一個視窗搶先拿到了銷售火車票(資源)的機會,就會把資源(火車票)鎖住(加鎖),讓其他視窗(執行緒)無法對該資源進行操作,這樣就不會出現亂套的現象了。只是加鎖 ,使用完畢釋放鎖,對效能好像有一定的影響,後面線上程的高階階段再來探討。

對票採取同步操作 鎖住this物件

package com.demo10;

public class Tickets extends Thread{
    private int lastTickets = 50;

    @Override
    public void run() {
        while (true) {
            synchronized (this){
                if(lastTickets > 0){
                    lastTickets--;
                    System.out.println(Thread.currentThread().getName() + 
                        "賣出一張,餘票:" + lastTickets + "張");
                }else{
                    System.out.println("票已經賣完了!");
                    Thread.currentThread().stop();
                }
            }
        }
    }

}

執行結果:

視窗A賣出一張,餘票:49張
視窗A賣出一張,餘票:48張
視窗A賣出一張,餘票:47張
視窗A賣出一張,餘票:46張
視窗A賣出一張,餘票:45張
視窗A賣出一張,餘票:44張
視窗A賣出一張,餘票:43張
視窗A賣出一張,餘票:42張
視窗A賣出一張,餘票:41張
視窗A賣出一張,餘票:40張
視窗A賣出一張,餘票:39張
視窗A賣出一張,餘票:38張
視窗A賣出一張,餘票:37張
視窗A賣出一張,餘票:36張
視窗A賣出一張,餘票:35張
視窗A賣出一張,餘票:34張
視窗A賣出一張,餘票:33張
視窗A賣出一張,餘票:32張
視窗A賣出一張,餘票:31張
視窗A賣出一張,餘票:30張
視窗A賣出一張,餘票:29張
視窗A賣出一張,餘票:28張
視窗A賣出一張,餘票:27張
視窗A賣出一張,餘票:26張
視窗A賣出一張,餘票:25張
視窗A賣出一張,餘票:24張
視窗A賣出一張,餘票:23張
視窗A賣出一張,餘票:22張
視窗A賣出一張,餘票:21張
視窗A賣出一張,餘票:20張
視窗A賣出一張,餘票:19張
視窗A賣出一張,餘票:18張
視窗A賣出一張,餘票:17張
視窗A賣出一張,餘票:16張
視窗A賣出一張,餘票:15張
視窗A賣出一張,餘票:14張
視窗A賣出一張,餘票:13張
視窗A賣出一張,餘票:12張
視窗A賣出一張,餘票:11張
視窗A賣出一張,餘票:10張
視窗A賣出一張,餘票:9張
視窗A賣出一張,餘票:8張
視窗A賣出一張,餘票:7張
視窗A賣出一張,餘票:6張
視窗A賣出一張,餘票:5張
視窗A賣出一張,餘票:4張
視窗A賣出一張,餘票:3張
視窗A賣出一張,餘票:2張
視窗A賣出一張,餘票:1張
視窗A賣出一張,餘票:0張
票已經賣完了!
票已經賣完了!
票已經賣完了!

對票採取同步操作 鎖住方法

package com.demo10;

public class Tickets extends Thread{
    private int lastTickets = 50;

    @Override
    public void run() {
        while (true) {
            sale();
        }
    }

    synchronized public void sale(){
        if(lastTickets > 0){
            lastTickets--;
            System.out.println(Thread.currentThread().getName() + 
                "賣出一張,餘票:" + lastTickets + "張");
        }else{
            System.out.println("票已經賣完了!");
            Thread.currentThread().stop();
        }
    }
}

執行結果:

視窗A賣出一張,餘票:49張
視窗A賣出一張,餘票:48張
視窗B賣出一張,餘票:47張
視窗B賣出一張,餘票:46張
視窗B賣出一張,餘票:45張
視窗B賣出一張,餘票:44張
視窗B賣出一張,餘票:43張
視窗B賣出一張,餘票:42張
視窗B賣出一張,餘票:41張
視窗B賣出一張,餘票:40張
視窗B賣出一張,餘票:39張
視窗B賣出一張,餘票:38張
視窗B賣出一張,餘票:37張
視窗C賣出一張,餘票:36張
視窗C賣出一張,餘票:35張
視窗C賣出一張,餘票:34張
視窗C賣出一張,餘票:33張
視窗C賣出一張,餘票:32張
視窗C賣出一張,餘票:31張
視窗C賣出一張,餘票:30張
視窗C賣出一張,餘票:29張
視窗C賣出一張,餘票:28張
視窗C賣出一張,餘票:27張
視窗C賣出一張,餘票:26張
視窗C賣出一張,餘票:25張
視窗C賣出一張,餘票:24張
視窗C賣出一張,餘票:23張
視窗C賣出一張,餘票:22張
視窗C賣出一張,餘票:21張
視窗C賣出一張,餘票:20張
視窗C賣出一張,餘票:19張
視窗C賣出一張,餘票:18張
視窗C賣出一張,餘票:17張
視窗C賣出一張,餘票:16張
視窗C賣出一張,餘票:15張
視窗C賣出一張,餘票:14張
視窗C賣出一張,餘票:13張
視窗C賣出一張,餘票:12張
視窗C賣出一張,餘票:11張
視窗C賣出一張,餘票:10張
視窗C賣出一張,餘票:9張
視窗C賣出一張,餘票:8張
視窗C賣出一張,餘票:7張
視窗C賣出一張,餘票:6張
視窗C賣出一張,餘票:5張
視窗C賣出一張,餘票:4張
視窗C賣出一張,餘票:3張
視窗C賣出一張,餘票:2張
視窗C賣出一張,餘票:1張
視窗C賣出一張,餘票:0張
票已經賣完了!
票已經賣完了!
票已經賣完了!