java多線程之火車(chē)售票系統(tǒng)模擬實(shí)例
1.前言
為了學(xué)習(xí)多線程共享與通信,我們模擬一個(gè)火車(chē)售票系統(tǒng),假設(shè)有10張火車(chē)票,三個(gè)窗口(也就是三個(gè)線程)同時(shí)進(jìn)行售票。
2.非同步代碼
package com.tl.skyLine.thread; /** * Created by tl on 17/3/6. */ public class SellTicket { public static void main(String[] args) { TicketWindow tw = new TicketWindow(); Thread t1 = new Thread(tw, "一號(hào)窗口"); Thread t2 = new Thread(tw, "二號(hào)窗口"); Thread t3 = new Thread(tw, "三號(hào)窗口"); t1.start(); t2.start(); t3.start(); } } class TicketWindow implements Runnable { private int tickets = 10; @Override public void run() { while (true) { if (tickets > 0) { System.out.println("還剩余票:" + tickets + "張"); tickets--; System.out.println(Thread.currentThread().getName() + "賣(mài)出一張火車(chē)票,還剩" + tickets + "張"); } else { System.out.println("余票不足,暫停出售!"); // wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用 try { Thread.sleep(1000 * 60 * 5); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
打印結(jié)果:
還剩余票:10張 還剩余票:10張 還剩余票:10張 二號(hào)窗口賣(mài)出一張火車(chē)票,還剩7張 還剩余票:7張 三號(hào)窗口賣(mài)出一張火車(chē)票,還剩8張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩9張 還剩余票:6張 還剩余票:6張 二號(hào)窗口賣(mài)出一張火車(chē)票,還剩6張 還剩余票:4張 三號(hào)窗口賣(mài)出一張火車(chē)票,還剩4張 還剩余票:3張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩5張 三號(hào)窗口賣(mài)出一張火車(chē)票,還剩2張 還剩余票:2張 三號(hào)窗口賣(mài)出一張火車(chē)票,還剩1張 還剩余票:1張 三號(hào)窗口賣(mài)出一張火車(chē)票,還剩0張 余票不足,暫停出售! 二號(hào)窗口賣(mài)出一張火車(chē)票,還剩3張 余票不足,暫停出售! 還剩余票:2張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩-1張 余票不足,暫停出售!
上面結(jié)果,可以清楚地看到,由于三個(gè)線程可以同時(shí)訪問(wèn)一個(gè)任務(wù),也就是售票任務(wù),會(huì)出現(xiàn)火車(chē)票還剩-1張這種不合實(shí)際的問(wèn)題,之所以出現(xiàn)是因?yàn)榧僭O(shè)在某一瞬間,tickets為1時(shí),tickets > 0為true,A線程運(yùn)行到tickets--這一行代碼,此時(shí)還沒(méi)有減去1,同時(shí)另外一個(gè)線程B剛好運(yùn)行到tickets > 0這一行代碼,判斷成功,開(kāi)始執(zhí)行賣(mài)票,此時(shí)A線程減去一張票,tickets=0,然后B線程又減去一張,則剩-1張。所以此時(shí)需要用到同步鎖synchronized。保證某一時(shí)刻只能有一個(gè)線程執(zhí)行售票功能。
3.同步代碼
package com.tl.skyLine.thread; /** * Created by tl on 17/3/6. */ public class SellTicket { public static void main(String[] args) { TicketWindow tw = new TicketWindow(); Thread t1 = new Thread(tw, "一號(hào)窗口"); Thread t2 = new Thread(tw, "二號(hào)窗口"); Thread t3 = new Thread(tw, "三號(hào)窗口"); t1.start(); t2.start(); t3.start(); } } class TicketWindow implements Runnable { private int tickets = 10; @Override public synchronized void run() { while (true) { if (tickets > 0) { System.out.println(Thread.currentThread().getName() + "準(zhǔn)備出票,還剩余票:" + tickets + "張"); tickets--; System.out.println(Thread.currentThread().getName() + "賣(mài)出一張火車(chē)票,還剩" + tickets + "張"); } else { System.out.println("余票不足,暫停出售!"); // wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用 try { Thread.sleep(1000 * 60 * 5); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
等同于:
class TicketWindow implements Runnable { private int tickets = 10; @Override public void run() { while (true) { synchronized (this) { if (tickets > 0) { System.out.println(Thread.currentThread().getName() + "準(zhǔn)備出票,還剩余票:" + tickets + "張"); tickets--; System.out.println(Thread.currentThread().getName() + "賣(mài)出一張火車(chē)票,還剩" + tickets + "張"); } else { System.out.println("余票不足,暫停出售!"); // wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用 try { Thread.sleep(1000 * 60 * 5); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
結(jié)果:
一號(hào)窗口準(zhǔn)備出票,還剩余票:10張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩9張 一號(hào)窗口準(zhǔn)備出票,還剩余票:9張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩8張 一號(hào)窗口準(zhǔn)備出票,還剩余票:8張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩7張 一號(hào)窗口準(zhǔn)備出票,還剩余票:7張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩6張 一號(hào)窗口準(zhǔn)備出票,還剩余票:6張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩5張 一號(hào)窗口準(zhǔn)備出票,還剩余票:5張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩4張 一號(hào)窗口準(zhǔn)備出票,還剩余票:4張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩3張 一號(hào)窗口準(zhǔn)備出票,還剩余票:3張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩2張 一號(hào)窗口準(zhǔn)備出票,還剩余票:2張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩1張 一號(hào)窗口準(zhǔn)備出票,還剩余票:1張 一號(hào)窗口賣(mài)出一張火車(chē)票,還剩0張 余票不足,暫停出售!
synchronized:
synchronized是Java中的關(guān)鍵字,是一種同步鎖。它修飾的對(duì)象有以下幾種:
1. 修飾一個(gè)代碼塊,被修飾的代碼塊稱為同步語(yǔ)句塊,其作用的范圍是大括號(hào){}括起來(lái)的代碼,作用的對(duì)象是調(diào)用這個(gè)代碼塊的對(duì)象;
2. 修飾一個(gè)方法,被修飾的方法稱為同步方法,其作用的范圍是整個(gè)方法,作用的對(duì)象是調(diào)用這個(gè)方法的對(duì)象;
3. 修改一個(gè)靜態(tài)的方法,其作用的范圍是整個(gè)靜態(tài)方法,作用的對(duì)象是這個(gè)類的所有對(duì)象;
4. 修改一個(gè)類,其作用的范圍是synchronized后面括號(hào)括起來(lái)的部分,作用主的對(duì)象是這個(gè)類的所有對(duì)象。
以上這篇java多線程之火車(chē)售票系統(tǒng)模擬實(shí)例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
本地MinIO存儲(chǔ)服務(wù)Java遠(yuǎn)程調(diào)用上傳文件的操作過(guò)程
MinIO是一款高性能、分布式的對(duì)象存儲(chǔ)系統(tǒng),它可以100%的運(yùn)行在標(biāo)準(zhǔn)硬件上,即X86等低成本機(jī)器也能夠很好的運(yùn)行MinIO,這篇文章主要介紹了本地MinIO存儲(chǔ)服務(wù)Java遠(yuǎn)程調(diào)用上傳文件的操作過(guò)程,需要的朋友可以參考下2023-11-11詳解Java編程的Observer觀察者設(shè)計(jì)模式
這篇文章主要介紹了Java編程的Observer觀察者設(shè)計(jì)模式,觀察者模式定義了一個(gè)一對(duì)多的依賴關(guān)系,讓一個(gè)或多個(gè)觀察者對(duì)象監(jiān)察一個(gè)主題對(duì)象,需要的朋友可以參考下2016-01-01Java對(duì)象轉(zhuǎn)JSON時(shí)動(dòng)態(tài)的增刪改查屬性詳解
這篇文章主要介紹了Java對(duì)象轉(zhuǎn)JSON時(shí)如何動(dòng)態(tài)的增刪改查屬性的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11SpringBoot靜態(tài)資源CSS等修改后再運(yùn)行無(wú)效的解決
這篇文章主要介紹了SpringBoot靜態(tài)資源CSS等修改后再運(yùn)行無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Mybatis-Plus處理Mysql?Json類型字段的詳細(xì)教程
這篇文章主要給大家介紹了關(guān)于Mybatis-Plus處理Mysql?Json類型字段的詳細(xì)教程,Mybatis-Plus可以很方便地處理JSON字段,在實(shí)體類中可以使用@JSONField注解來(lái)標(biāo)記JSON字段,同時(shí)在mapper.xml中使用json函數(shù)來(lái)操作JSON字段,需要的朋友可以參考下2024-01-01Java實(shí)現(xiàn)將PDF轉(zhuǎn)為PDF/A
通過(guò)將PDF格式轉(zhuǎn)換為PDF/A格式,可保護(hù)文檔布局、格式、字體、大小等不受更改,從而實(shí)現(xiàn)文檔安全保護(hù)的目的,同時(shí)又能保證文檔可讀、可訪問(wèn)。本文將為大家介紹如何實(shí)現(xiàn)這一轉(zhuǎn)換,需要的可以參考一下2022-01-01spring boot(三)之Spring Boot中Redis的使用
這篇文章主要介紹了spring boot(三)之Spring Boot中Redis的使用,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-05-05SpringBoot整合WebSocket實(shí)現(xiàn)后端向前端發(fā)送消息的實(shí)例代碼
WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于SpringBoot整合WebSocket實(shí)現(xiàn)后端向前端發(fā)送消息的相關(guān)資料,需要的朋友可以參考下2023-03-03