<p id="m2nkj"><option id="m2nkj"><big id="m2nkj"></big></option></p>
    <strong id="m2nkj"></strong>
    <ruby id="m2nkj"></ruby>

    <var id="m2nkj"></var>
  • 教你用三種方式模擬兩個(gè)線程搶票

    共 9682字,需瀏覽 20分鐘

     ·

    2024-06-25 09:19

    往期熱門文章:

    
        

    1、MySQL中varchar(50)和varchar(500)區(qū)別是什么?

    2、頂級(jí)Javaer都在使用的類庫,真香!

    3、最適合程序員的畫圖工具?

    4、Logback 與 log4j2 性能哪個(gè)更強(qiáng)?

    5、只用Tomcat,不用Nginx行不行?


    前言

    在多線程編程中,資源競爭是一個(gè)常見的問題。資源競爭發(fā)生在多個(gè)線程試圖同時(shí)訪問或修改共享資源時(shí),可能導(dǎo)致數(shù)據(jù)不一致或其他并發(fā)問題。在模擬兩個(gè)線程搶票的場景中,我們需要考慮如何公平地分配票,并確保每個(gè)線程都有機(jī)會(huì)成功獲取票。

    本篇文章將通過三種方式來模擬兩個(gè)線程搶票的過程,以展示不同的并發(fā)控制策略。

    這三種方式包括:

    • 使用 Synchronized 來確保一次只有一個(gè)線程可以訪問票資源。

    • 使用 ReentrantLock 來實(shí)現(xiàn)線程間的協(xié)調(diào)。

    • 使用 Semaphore 來限制同時(shí)訪問票的線程數(shù)量。


    通過比較這三種方式,我們可以深入了解并發(fā)控制的不同實(shí)現(xiàn)方式及其優(yōu)缺點(diǎn)。在實(shí)際應(yīng)用中,需要根據(jù)具體場景和需求選擇合適的并發(fā)控制策略。

    此外,為了更直觀地展示搶票過程,我們將使用代碼來描述每種方式的實(shí)現(xiàn)邏輯。

    一、Synchronized

    含義:Synchronized 是 Java 中的一個(gè)關(guān)鍵字,用于實(shí)現(xiàn)線程同步。當(dāng)一個(gè)方法或代碼塊被 Synchronized 修飾時(shí),同一時(shí)間只能有一個(gè)線程可以執(zhí)行這個(gè)方法或代碼塊。


    代碼如下:
    static class TicketSystemBySynchronized {  private int tickets = 100;
    public void sellTicket() { while (tickets > 0) { //還有票時(shí)進(jìn)行循環(huán) synchronized (this) { try { if (tickets > 0) System.out.println(Thread.currentThread().getName() + "賣出一張票,剩余票數(shù):" + --tickets); Thread.sleep(200); //模擬售票 } catch (InterruptedException e) { e.printStackTrace(); } } } }}
    這個(gè)類中有一個(gè)私有的整型變量 tickets,表示票的總數(shù),初始值為 100。

    類中有一個(gè)公共方法 sellTicket(),這個(gè)方法模擬售票過程。當(dāng)還有票(tickets > 0)時(shí),會(huì)進(jìn)入一個(gè) while 循環(huán)。在循環(huán)中,首先通過 synchronized (this) 對(duì)當(dāng)前對(duì)象進(jìn)行同步,保證同一時(shí)間只有一個(gè)線程可以執(zhí)行以下代碼塊。

    在同步代碼塊中,首先檢查票的數(shù)量是否大于0。如果是,則輸出當(dāng)前線程的名稱以及售出的票數(shù)和剩余票數(shù)。然后,通過 --tickets 操作將票的數(shù)量減1。

    接下來,線程休眠 200 毫秒(模擬售票過程)。休眠結(jié)束后,循環(huán)繼續(xù)執(zhí)行,直到票的數(shù)量為 0。

    二、ReentrantLock

    含義:ReentrantLock,也稱為可重入鎖,是一種遞歸無阻塞的同步機(jī)制。它可以等同于 synchronized 的使用,但是 ReentrantLock 提供了比 synchronized 更強(qiáng)大、靈活的鎖機(jī)制,可以減少死鎖發(fā)生的概率。


    代碼如下:
    static class TicketSystemByReentrantLock {  private int tickets = 100;
    private final ReentrantLock lock = new ReentrantLock(); //定義鎖
    public void sellTicket() { while (tickets > 0) { lock.lock(); //上鎖 try { Thread.sleep(200); //模擬售票 if (tickets > 0) System.out.println(Thread.currentThread().getName() + "賣出一張票,剩余票數(shù):" + --tickets); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); //解鎖 } } }}
    這個(gè)類中有一個(gè)私有的整型變量 tickets,表示票的總數(shù),初始值為 100。另外定義了一個(gè)私有的 final 類型的 ReentrantLock 對(duì)象 lock,這個(gè)對(duì)象用于控制對(duì)共享資源的訪問。

    類中有一個(gè)公共方法 sellTicket(),這個(gè)方法模擬售票過程。當(dāng)還有票(tickets > 0)時(shí),會(huì)進(jìn)入一個(gè) while 循環(huán)。在循環(huán)中,首先通過 lock.lock() 獲取鎖,保證同一時(shí)間只有一個(gè)線程可以執(zhí)行以下代碼塊。

    在鎖保護(hù)的代碼塊中,首先線程休眠 200 毫秒(模擬售票過程)。然后檢查票的數(shù)量是否大于 0。如果是,則輸出當(dāng)前線程的名稱以及售出的票數(shù)和剩余票數(shù)。然后,通過 --tickets 操作將票的數(shù)量減 1。

    最后,都會(huì)通過 lock.unlock() 釋放鎖。防止死鎖!

    三、Semaphore

    含義:Semaphore 是一種計(jì)數(shù)信號(hào)量,用于管理一組資源。它是一種在多線程環(huán)境下使用的設(shè)施,該設(shè)施負(fù)責(zé)協(xié)調(diào)各個(gè)線程,以保證它們能夠正確、合理地使用公共資源。Semaphore 內(nèi)部基于 AQS(Abstract Queued Synchronizer)的共享模式,相當(dāng)于給線程規(guī)定一個(gè)量從而控制允許活動(dòng)的線程數(shù)。


    代碼如下:


    static class TicketSystemBySemaphore { private final Semaphore semaphore;
    public TicketSystemBySemaphore() { this.semaphore = new Semaphore(100); //總共100張票 }
    public void sellTicket() { int i = semaphore.availablePermits(); //返回此信號(hào)量中當(dāng)前可用的許可證數(shù)
    while (i > 0) { try { Thread.sleep(200); semaphore.acquire(); // 獲取信號(hào)量,如果信號(hào)量為0,線程將阻塞等待 System.out.println( Thread.currentThread().getName() + "賣出一張票,剩余票數(shù):" + --i); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { semaphore.release(); // 釋放信號(hào)量,允許其他線程獲取信號(hào)量 } } }}

    Semaphore 是一個(gè)計(jì)數(shù)信號(hào)量,用于控制資源的并發(fā)訪問。在構(gòu)造函數(shù)中,初始化了這個(gè) Semaphore,設(shè)置總的可用票數(shù)為 100。

    sellTicket() 方法模擬售票過程。首先獲取當(dāng)前可用的票數(shù),然后進(jìn)入一個(gè) while 循環(huán),只要還有可用的票,就會(huì)嘗試獲取一個(gè)票。如果當(dāng)前沒有可用的票,線程將會(huì)阻塞等待。一旦獲取了票,就輸出售出的信息。最后釋放信號(hào)量。

    四、抽象工廠模式優(yōu)化

    含義:抽象工廠模式是一種創(chuàng)建型設(shè)計(jì)模式,它為創(chuàng)建一系列相關(guān)或互相依賴的對(duì)象提供了一種最佳解決方案。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。



    因?yàn)橐獙?duì)三種實(shí)現(xiàn)類型的代碼進(jìn)行測試,不想多寫 if...else... 的代碼,不想每次指定創(chuàng)建的對(duì)象,也為了防止以后有更多實(shí)現(xiàn)方法的不方便。提高代碼的可維護(hù)性和可擴(kuò)展性。

    所以這里采用抽象工廠模式來進(jìn)行優(yōu)化。

    代碼如下:

    首先實(shí)現(xiàn)一個(gè)接口類:
    public interface TicketSystem {    void sellTicket();}

    因?yàn)槿齻€(gè)模擬實(shí)現(xiàn)中都定義了 sellTicket 這個(gè)方法,所以在接口類里面定義一個(gè)方法,然后由實(shí)現(xiàn)類去重寫該方法。

    接下來實(shí)現(xiàn)靜態(tài)工廠類:
    static class CodeSandboxFactory {  static TicketSystem newInstance(String type) {    switch (type) {      case "Synchronized":        return new TicketSystemBySynchronized();      case "ReentrantLock":        return new TicketSystemByReentrantLock();      case "Semaphore":      default:        return new TicketSystemBySemaphore();    }  }}

    這個(gè) CodeSandboxFactory 類是一個(gè)靜態(tài)工廠類,用于創(chuàng)建TicketSystem對(duì)象的不同實(shí)例。它接受一個(gè)字符串參數(shù) type,根據(jù)該參數(shù)的值決定創(chuàng)建哪種類型的TicketSystem 對(duì)象。

    • 如果type參數(shù)的值為"Synchronized",則返回一個(gè)新的 TicketSystemBySynchronized對(duì)象;

    • 如果type參數(shù)的值為"ReentrantLock",則返回一個(gè)新的 TicketSystemByReentrantLock 對(duì)象;

    • 如果type參數(shù)的值為"Semaphore",則返回一個(gè)新的 TicketSystemBySemaphore對(duì)象;

    • 如果type參數(shù)的值不是以上三種之一,則默認(rèn)返回一個(gè)新的TicketSystemBySemaphore 對(duì)象。


    這種設(shè)計(jì)使得客戶端代碼可以方便地通過傳遞不同的類型字符串來獲取不同類型的 TicketSystem 對(duì)象,而不需要關(guān)心這些對(duì)象的實(shí)際創(chuàng)建過程。

    這有助于降低客戶端代碼與具體實(shí)現(xiàn)之間的耦合度,提高代碼的可維護(hù)性和可擴(kuò)展性。

    五、整體代碼 

    代碼如下:

    public class ThreadsGrabTickets {  public static void main(String[] args) {    TicketSystem system = CodeSandboxFactory.newInstance("Synchronized");    //        TicketSystem system =    //        CodeSandboxFactory.newInstance("ReentrantLock"); TicketSystem    //        system = CodeSandboxFactory.newInstance("Semaphore");
    new Thread(system::sellTicket, "線程1").start(); new Thread(system::sellTicket, "線程2").start(); }
    static class CodeSandboxFactory { static TicketSystem newInstance(String type) { switch (type) { case "Synchronized": return new TicketSystemBySynchronized(); case "ReentrantLock": return new TicketSystemByReentrantLock(); case "Semaphore": default: return new TicketSystemBySemaphore(); } } }
    static class TicketSystemBySynchronized implements TicketSystem { private int tickets = 100;
    @Override public void sellTicket() { while (tickets > 0) { synchronized (this) { try { if (tickets > 0) System.out.println(Thread.currentThread().getName() + "賣出一張票,剩余票數(shù):" + --tickets); Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
    static class TicketSystemByReentrantLock implements TicketSystem { private int tickets = 100;
    private final ReentrantLock lock = new ReentrantLock(); //定義鎖
    @Override public void sellTicket() { while (tickets > 0) { lock.lock(); //上鎖 try { Thread.sleep(200); //模擬售票 if (tickets > 0) System.out.println(Thread.currentThread().getName() + "賣出一張票,剩余票數(shù):" + --tickets); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); //解鎖 } } } }
    static class TicketSystemBySemaphore implements TicketSystem { private final Semaphore semaphore;
    public TicketSystemBySemaphore() { this.semaphore = new Semaphore(100); //總共100張票 }
    @Override public void sellTicket() { int i = semaphore.availablePermits(); //返回此信號(hào)量中當(dāng)前可用的許可證數(shù)
    while (i > 0) { try { Thread.sleep(200); semaphore.acquire(); // 獲取信號(hào)量,如果信號(hào)量為0,線程將阻塞等待 System.out.println(Thread.currentThread().getName() + "賣出一張票,剩余票數(shù):" + --i); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { semaphore.release(); // 釋放信號(hào)量,允許其他線程獲取信號(hào)量 } } } }}

    六、總結(jié)

    本文通過模擬兩個(gè)線程搶票的場景,展示了三種不同的并發(fā)控制策略:使用 Synchronized、ReentrantLock 和 Semaphore。

    通過比較這三種方式,我們可以深入了解并發(fā)控制的不同實(shí)現(xiàn)方式。

    在實(shí)際應(yīng)用中,需要根據(jù)具體場景和需求選擇合適的并發(fā)控制策略。

    轉(zhuǎn)自:綠皮龜,

    鏈接:blog.csdn.net/kologin/article/details/135953580


       
    往期熱門文章:

    1、聽說你還在用Xshell?
    2、驚艷到我的 10個(gè) MySQL高級(jí)查詢技巧!
    3、我有點(diǎn)想用JDK17了
    4、解放大腦:ChatGPT + PlantUML = 不用畫圖了
    5、高逼格的SQL寫法:行行比較
    6、限流算法哪家強(qiáng)?時(shí)間窗口,令牌桶與漏桶算法對(duì)比
    7、每天都提交代碼,那你知道.git目錄內(nèi)部的秘密嗎?
    8、我患上了空指針后遺癥
    9、這10個(gè)小技巧讓你減少80%的Bug!
    10、升級(jí) JDK17 一個(gè)不可拒絕的理由

    瀏覽 169
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    <p id="m2nkj"><option id="m2nkj"><big id="m2nkj"></big></option></p>
    <strong id="m2nkj"></strong>
    <ruby id="m2nkj"></ruby>

    <var id="m2nkj"></var>
  • 乳大有奶水风流少妇 | 国产精品 视频瘾无码 | 青青青国产 | 无码人妻精品一区二区在线 | 69网站 | 成人精品电影久久 | 免费亚洲视频 | 色拍拍视频 | 97人人操 | 台湾一级高清 |