第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

多線程售票案例

1. 前言

本節(jié)內(nèi)容主要是使用 Java 的鎖機(jī)制對多線程售票案例進(jìn)行實(shí)現(xiàn)。售票案例多數(shù)情況下主要關(guān)注多線程如何安全的減少庫存,也就是剩余的票數(shù),當(dāng)票數(shù)為 0 時(shí),停止減少庫存。

本節(jié)內(nèi)容除了關(guān)注車票庫存的減少,還會涉及到退票窗口,能夠更加貼切的模擬真實(shí)的場景。

本節(jié)內(nèi)容需要學(xué)習(xí)者關(guān)注如下兩個(gè)重點(diǎn):

  • 掌握多線程的售票機(jī)制模型,在后續(xù)的工作中如果涉及到類似的場景,能夠第一時(shí)間了解場景的整體結(jié)構(gòu);
  • 使用 Condition 和 Lock 實(shí)現(xiàn)售票機(jī)制,鞏固我們本章節(jié)內(nèi)容所學(xué)習(xí)的新的鎖機(jī)制。

2. 售票機(jī)制模型

售票機(jī)制模型是源于現(xiàn)實(shí)生活中的售票場景,從開始的單窗口售票到多窗口售票,從開始的人工統(tǒng)計(jì)票數(shù)到后續(xù)的系統(tǒng)智能在線售票。多并發(fā)編程能夠?qū)崿F(xiàn)這一售票場景,多窗口售票情況下保證線程的安全性和票數(shù)的正確性。

圖片描述

如上圖所示,有兩個(gè)售票窗口進(jìn)行售票,有一個(gè)窗口處理退票,這既是現(xiàn)實(shí)生活中一個(gè)簡單的售票機(jī)制。

3. 售票機(jī)制實(shí)現(xiàn)

場景設(shè)計(jì)

  • 創(chuàng)建一個(gè)工廠類 TicketCenter,該類包含兩個(gè)方法,saleRollback 退票方法和 sale 售票方法;
  • 定義一個(gè)車票總數(shù)等于 10 ,為了方便觀察結(jié)果,設(shè)置為 10。學(xué)習(xí)者也可自行選擇數(shù)量;
  • 對于 saleRollback 方法,當(dāng)發(fā)生退票時(shí),通知售票窗口繼續(xù)售賣車票;
  • 對 saleRollback 進(jìn)行特別設(shè)置,每隔 5000 毫秒退回一張車票;
  • 對于 sale 方法,只要有車票就進(jìn)行售賣。為了更便于觀察結(jié)果,每賣出一張車票,sleep 2000 毫秒;
  • 創(chuàng)建一個(gè)測試類,main 函數(shù)中創(chuàng)建 2 個(gè)售票窗口和 1 個(gè)退票窗口,運(yùn)行程序進(jìn)行結(jié)果觀察。
  • 修改 saleRollback 退票時(shí)間,每隔 25 秒退回一張車票;
  • 再次運(yùn)行程序并觀察結(jié)果。

實(shí)現(xiàn)要求:本實(shí)驗(yàn)要求使用 ReentrantLock 與 Condition 接口實(shí)現(xiàn)同步機(jī)制。

實(shí)例

public class DemoTest {
        public static void main(String[] args) {
            TicketCenter ticketCenter = new TicketCenter();
            new Thread(new saleRollback(ticketCenter),"退票窗口"). start();
            new Thread(new Consumer(ticketCenter),"1號售票窗口"). start();
            new Thread(new Consumer(ticketCenter),"2號售票窗口"). start();
        }
}

class TicketCenter {
    private int capacity = 10; // 根據(jù)需求:定義10漲車票
    private Lock lock = new ReentrantLock(false);
    private Condition saleLock = lock.newCondition();
    // 根據(jù)需求:saleRollback 方法創(chuàng)建,為退票使用
    public void saleRollback() {
        try {
            lock.lock();
            capacity++;
            System.out.println("線程("+Thread.currentThread().getName() + ")發(fā)生退票。" + "當(dāng)前剩余票數(shù)"+capacity+"個(gè)");
            saleLock.signalAll(); //發(fā)生退票,通知售票窗口進(jìn)行售票
        } finally {
            lock.unlock();
        }
    }

    // 根據(jù)需求:sale 方法創(chuàng)建
    public void sale() {
        try {
            lock.lock();
            while (capacity==0) { //沒有票的情況下,停止售票
                try {
                    System.out.println("警告:線程("+Thread.currentThread().getName() + ")準(zhǔn)備售票,但當(dāng)前沒有剩余車票");
                    saleLock.await(); //剩余票數(shù)為 0 ,無法售賣,進(jìn)入 wait
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            capacity-- ; //如果有票,則售賣 -1
            System.out.println("線程("+Thread.currentThread().getName() + ")售出一張票。" + "當(dāng)前剩余票數(shù)"+capacity+"個(gè)");
        } finally {
            lock.unlock();
        }
    }
}

class saleRollback implements Runnable {
    private TicketCenter TicketCenter; //關(guān)聯(lián)工廠類,調(diào)用 saleRollback 方法
    public saleRollback(TicketCenter TicketCenter) {
        this.TicketCenter = TicketCenter;
    }
    public void run() {
        while (true) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            TicketCenter.saleRollback(); //根據(jù)需求 ,調(diào)用 TicketCenter 的 saleRollback 方法

        }
    }
}
class Consumer implements Runnable {
    private TicketCenter TicketCenter;
    public Consumer(TicketCenter TicketCenter) {
        this.TicketCenter = TicketCenter;
    }
    public void run() {
        while (true) {
            TicketCenter.sale(); //調(diào)用sale 方法
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

結(jié)果驗(yàn)證

線程(1號售票窗口)售出一張票。當(dāng)前剩余票數(shù)9個(gè)
線程(2號售票窗口)售出一張票。當(dāng)前剩余票數(shù)8個(gè)
線程(2號售票窗口)售出一張票。當(dāng)前剩余票數(shù)7個(gè)
線程(1號售票窗口)售出一張票。當(dāng)前剩余票數(shù)6個(gè)
線程(1號售票窗口)售出一張票。當(dāng)前剩余票數(shù)5個(gè)
線程(2號售票窗口)售出一張票。當(dāng)前剩余票數(shù)4個(gè)
線程(退票窗口)發(fā)生退票。當(dāng)前剩余票數(shù)5個(gè)
線程(1號售票窗口)售出一張票。當(dāng)前剩余票數(shù)4個(gè)
線程(2號售票窗口)售出一張票。當(dāng)前剩余票數(shù)3個(gè)
線程(2號售票窗口)售出一張票。當(dāng)前剩余票數(shù)2個(gè)
線程(1號售票窗口)售出一張票。當(dāng)前剩余票數(shù)1個(gè)
線程(退票窗口)發(fā)生退票。當(dāng)前剩余票數(shù)2個(gè)
線程(1號售票窗口)售出一張票。當(dāng)前剩余票數(shù)1個(gè)
線程(2號售票窗口)售出一張票。當(dāng)前剩余票數(shù)0個(gè)
警告:線程(1號售票窗口)準(zhǔn)備售票,但當(dāng)前沒有剩余車票
警告:線程(2號售票窗口)準(zhǔn)備售票,但當(dāng)前沒有剩余車票
線程(退票窗口)發(fā)生退票。當(dāng)前剩余票數(shù)1個(gè)
線程(1號售票窗口)售出一張票。當(dāng)前剩余票數(shù)0個(gè)
警告:線程(2號售票窗口)準(zhǔn)備售票,但當(dāng)前沒有剩余車票
警告:線程(1號售票窗口)準(zhǔn)備售票,但當(dāng)前沒有剩余車票

結(jié)果分析:從結(jié)果來看,我們正確的完成了售票和退票的機(jī)制,并且使用了 ReentrantLock 與 Condition 接口。

代碼片段分析 1:看售票方法代碼。

public void sale() {
        try {
            lock.lock();
            while (capacity==0) { //沒有票的情況下,停止售票
                try {
                    System.out.println("警告:線程("+Thread.currentThread().getName() + ")準(zhǔn)備售票,但當(dāng)前沒有剩余車票");
                    saleLock.await(); //剩余票數(shù)為 0 ,無法售賣,進(jìn)入 wait
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            capacity-- ; //如果有票,則售賣 -1
            System.out.println("線程("+Thread.currentThread().getName() + ")售出一張票。" + "當(dāng)前剩余票數(shù)"+capacity+"個(gè)");
        } finally {
            lock.unlock();
        }
    }

主要來看方法中僅僅使用了 await 方法,因?yàn)橥似笔菆鼍坝|發(fā)的,售票窗口無需喚醒退票窗口,因?yàn)檎鎸?shí)的場景下,可能沒有退票的發(fā)生,所以無需喚醒。這與生產(chǎn)者與消費(fèi)者模式存在著比較明顯的區(qū)別。

代碼片段分析 2:看退票方法代碼。

public void saleRollback() {
        try {
            lock.lock();
            capacity++;
            System.out.println("線程("+Thread.currentThread().getName() + ")發(fā)生退票。" + "當(dāng)前剩余票數(shù)"+capacity+"個(gè)");
            saleLock.signalAll(); //發(fā)生退票,通知售票窗口進(jìn)行售票
        } finally {
            lock.unlock();
        }
    }

退票方法只有 signalAll 方法,通知售票窗口進(jìn)行售票,無需調(diào)用 await 方法,因?yàn)橹灰型似钡陌l(fā)生,就能夠繼續(xù)售票,沒有庫存上限的定義,這也是與生產(chǎn)者與消費(fèi)者模式的一個(gè)主要區(qū)別。

總結(jié):售票機(jī)制與生產(chǎn)者 - 消費(fèi)者模式存在著細(xì)微的區(qū)別,需要學(xué)習(xí)者通過代碼的實(shí)現(xiàn)慢慢體會。由于售票方法只需要進(jìn)入 await 狀態(tài),退票方法需要喚醒售票的 await 狀態(tài),因此只需要創(chuàng)建一個(gè)售票窗口的 Condition 對象。

4. 小結(jié)

本節(jié)內(nèi)容主要對售票機(jī)制模型進(jìn)行了講解,核心內(nèi)容為售票機(jī)制的實(shí)現(xiàn)。實(shí)現(xiàn)的過程使用 ReentrantLock 與 Condition 接口實(shí)現(xiàn)同步機(jī)制,也是本節(jié)課程的重點(diǎn)知識。

至此,并發(fā)編程原理課程就結(jié)束了,從基本的多線程的創(chuàng)建,synchronized 關(guān)鍵字的使用,再到鎖的應(yīng)用,貫穿了并發(fā)編程原理的全部知識,學(xué)習(xí)完本套課程,可以進(jìn)一步學(xué)習(xí)并發(fā)編程包的課程。