1. 程式人生 > >Java多視窗賣票問題詳解

Java多視窗賣票問題詳解

Java多視窗賣票問題詳解

Java

在練習Java多執行緒的過程中,通常都會通過多視窗賣票這個問題來詳細逐漸解析多執行緒的執行緒同步,其中涉及到同步程式碼塊同步方法互斥鎖

鐵道部發布了一個售票任務,銷售1000張票,要求有10個視窗來進行銷售,請編寫多執行緒程式來模擬這個效果。

第一步

class Test4Thread extends Thread {
    private static int tickets = 1000;

    @Override
    public void
run()
{ while (tickets > 0) { System.out.println(this.getName() + "正在銷售第" + (tickets--) + "張票!"); } } } public class Test4 { public static void main(String[] args) { for (int i = 0; i < 4; i++) { new Test4Thread().start(); } } }

執行程式碼發現存量重複賣票問題,因為多個執行緒同時執行時,就會出現多個執行緒獲取到的變數值是相同的,導致出現重複賣票問題。

重複賣票問題
重複賣票問題

第二步

@Override
    public void run() {
        while (tickets > 0) {
            synchronized ("lock") {
                System.out.println(this
.getName() + "正在銷售第" + (tickets--) + "張票!"); } } }

這裡雖然在迴圈體內部增加了同步程式碼塊,但是可能在票數為1時,可能會出現多個執行緒進入迴圈體排隊執行程式碼塊的情況,這樣就會出現買負數票的問題。

出現賣負數票問題
賣負數票問題

第三步

    public void run() {
        while (true) {
            synchronized ("lock") {
                if (tickets>0) {
                    System.out.println(this.getName() + "正在銷售第" + (tickets--) + "張票!");
                }else {
                    break;
                }
            }
        }
    }

執行緒類的run方法經過修改後程式碼結果符合預期。

最終執行結果
最終執行結果

最終程式碼:

class Test4Thread extends Thread {
    private static int tickets = 1000;

    @Override
    public void run() {
        while (true) {
            synchronized ("lock") {
                if (tickets>0) {
                    System.out.println(this.getName() + "正在銷售第" + (tickets--) + "張票!");
                }else {
                    break;
                }
            }
        }
    }
}

public class Test4 {
    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            new Test4Thread().start();
        }
    }
}

利用同步方法來處理賣票問題:

Java除了利用同步程式碼塊之外,還可以利用同步方法來處理問題,利用同步方法時仍要要注意呼叫同步方法是他一個物件,所以繼承的執行緒類需要增加static關鍵字,同樣需要通過同步方法的返回值來判斷執行緒票是否賣完。

class Test5Thread extends Thread {
    private static int tickets = 1000;

    @Override
    public void run() {
        while (test()) {
        }
    }

    public static synchronized boolean test() {
        if (tickets > 0) {
            System.out.println(Thread.currentThread().getName() + "正在銷售第" + (tickets--) + "張票!");
            return true;// 表示票還沒有賣完
        } else {
            System.out.println("票已經賣完了");
            return false;// 表示票已經賣完
        }
    }
}

public class Test5 {
    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            new Test5Thread().start();
        }
    }
}

利用互斥鎖來解決賣票問題:

除了利用同步程式碼塊和同步方法外,在Java裡面還可以利用互斥鎖來處理,在用互斥鎖處理的過程中,為了避免出現死鎖問題,需要利用try-finally來互斥鎖,將結果程式碼放在finally程式碼塊中。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Test6Thread extends Thread {
    private static int tickets = 1000;
    private static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (tickets > 0) {
                    System.out.println(this.getName() + "正在銷售第" + (tickets--) + "張票!");
                } else {
                    System.out.println("票已經賣完了");
                    break;
                }

            } finally {
                lock.unlock();
            }

        }
    }
}
public class Test6 {
    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            new Test6Thread().start();
        }
    }
}