天天看點

使用同步解決多人賣票問題 | 帶你學《Java語言進階特性》之九

上一篇:迅速讀懂Java線程的交通規則 | 帶你學《Java語言進階特性》之八

【本節目标】

通過閱讀本節内容,你将了解到線程同步問題出現的原因,并學會使用synchronized關鍵字實作多個線程同時隻有一個能進行調用的限制,解決線程同步問題。

在多線程的處理之中,可以利用Runnable描述多個線程操作的資源,而Thread描述每一個線程對象,當然多個線程通路同一資源時如果處理不當就會産生資料的錯誤操作。

同步問題的引出

下面編寫一個簡單的賣票程式,将若幹個線程對象實作賣票的處理操作。

範例:實作賣票操作

class MyThread implements Runnable {
    private int ticket = 10 ;        //總票數為10張
    @Override
    public void run() {
        while (true) {
            if (this.ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--) ;
            } else {
                System.out.println("***** 票已經賣光了 *****") ;
                break ;
            }
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt=new MyThread() ;
        new Thread(mt,"票販子A").start() ;
        new Thread(mt,"票販子B").start() ;
        new Thread(mt,"票販子C").start() ;
    }
}           
使用同步解決多人賣票問題 | 帶你學《Java語言進階特性》之九

圖一 賣票程式執行

此時的程式将建立3個線程對象,并且這三個線程将進行10張票的出售。此時的程式在進行賣票處理的時候,并沒有任何的問題(假象),下面可以模拟一下賣票中的延遲操作。

class MyThread implements Runnable {
    private int ticket = 10;       //總票數為10張
    @Override
    public void run() {
        while (true) {
            if (this.ticket > 0) {
                try {
                    Thread.sleep(100);    //模拟網絡延遲
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--);
            } else {
                System.out.println("***** 票已經賣光了 *****");
                break;
            }
        }
    }
}           
使用同步解決多人賣票問題 | 帶你學《Java語言進階特性》之九

圖二 賣票問題執行操作

這個時候追加了延遲,問題就暴露出來了,而實際上這個問題一直都在。

使用同步解決多人賣票問題 | 帶你學《Java語言進階特性》之九

圖三 賣票處理

線程同步處理

經過分析之後已經可以确定同步問題産生的主要原因了,那麼下面就需要進行同步問題的解決,但是解決同步問題的關鍵是鎖,指的是當某一個線程執行操作的時候,其他線程外面等待;

使用同步解決多人賣票問題 | 帶你學《Java語言進階特性》之九

圖四 問題的解決

如果要想程式中實作這把鎖功能,就可以使用synchronized關鍵字來實作,利用此關鍵字可以定義同步方法或同步代碼塊,在同步代碼塊的操作中的代碼隻允許一個線程執行。

1、利用同步代碼塊進行處理:

synchronized (同步對象){
    同步代碼操作;
}           

一般要進行同步對象處理的時候可以采用目前對象this進行同步。

範例:利用同步代碼塊解決資料同步通路問題

class MyThread implements Runnable {
    private int ticket = 10 ;    //總票數為10張
    @Override
    public void run() {
        while (true) {
            synchronized (this) {    //每一次隻允許一個線程進行通路
                if (this.ticket > 0) {
                    try {
                        Thread.sleep(100);     //模拟網絡延遲
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--);
                } else {
                    System.out.println("***** 票已經賣光了 *****");
                    break;
                }
            }
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();
        new Thread(mt, "票販子A").start();
        new Thread(mt, "票販子B").start();
        new Thread(mt, "票販子C").start();
    }
}           

加入同步處理之後,程式的整體性能下降了。同步實際上會造成性能的降低。

2、利用同步方法解決:隻需要在方法定義上使用synchronized關鍵字即可。

class MyThread implements Runnable {
    private int ticket = 10;    //總票數為10張
    public synchronized boolean sale() {
        if (this.ticket > 0) {
            try {
                Thread.sleep(100);    //模拟網絡延遲
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--);
            return true;
        } else {
            System.out.println("***** 票已經賣光了 *****");
            return false;
        }
    }
    @Override
    public void run() {
        while ( this.sale()) {}
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();
        new Thread(mt, "票販子A").start();
        new Thread(mt, "票販子B").start();
        new Thread(mt, "票販子C").start();
    }
}           
使用同步解決多人賣票問題 | 帶你學《Java語言進階特性》之九

圖五 解決同步問題

在日後學習Java類庫的時候,系統中許多的類上使用的同步處理采用的都是同步方法。

想學習更多的Java的課程嗎?從小白到大神,從入門到精通,更多精彩不容錯過!免費為您提供更多的學習資源。

本内容視訊來源于

阿裡雲大學 下一篇:同步的缺陷-死鎖問題 | 帶你學《Java語言進階特性》之十 更多Java面向對象程式設計文章檢視此處