您好,登錄后才能下訂單哦!
怎么在Java中利用多線程模擬站點售票?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
哦吼,這次的實驗題目是一道非常經典的多線程買票問題。題目要求我們創建5個線程來模擬賣票,當然這其中就包含多線程存在也就是我們要解決的問題,重復賣票和超額賣票。即多個窗口賣出同一張票以及窗口賣出非正數編號的票。
不過這個問題可以先放一下,我們先來創建基礎的線程模型,并在主方法中創建五個線程讓他們跑起來;
話不多說,上代碼。
public class Ticket { public static void main(String[] args) { for(int i = 1;i <= 5;i++) { //創建5個線程并啟動他們 //注意一定要使用Thread類創建線程并使用start方法啟動 //而不是直接創建TicketSeller對象調用run方法!!!!!! new Thread(new TicketSeller(i)).start(); } } } //售票類,實現Runnable接口,可以作為線程執行對象 class TicketSeller implements Runnable{ //該售票窗口編號 private int code; public TicketSeller(int code) { this.code = code; } @Override public void run() { for(int i = 0;i < 5;i++) { System.out.println(code + "號窗口"); //為了使線程能夠交替執行,打印完成語句讓線程休眠一小會 try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } }
代碼的含義和需要注意的點都在注釋里面了,一定要看注釋!!!
運行結果就是:
后面太長了就不放了。。。。
完成了基礎的多線程框架搭建后,我們來為每個線程執行過程中加入賣票的程序
首先要解決的一個問題是:票存在哪里?。毋庸置疑的是由于是多線程并發的售票,因此票這個變量一定是被多個線程所共享的,而不能是每個線程對象自己的屬性。
一個可行的方案是在TicketSellet
類中定義靜態的票計數,這樣所有的線程訪問票的時候訪問的都是同一個票計數變量。
另一個可行方案是使用一個對象管理票,票計數是這個對象的成員,并且讓每個TicketSeller
持有相同的對象。那么多個線程也同樣共享票計數。
當然,可行的方案還有很多,現在我們先來實現第一種,在之后的改進中,我們還會用到第二種。
先來一個沒有加鎖的寫法,看看他的問題
//售票類,實現Runnable接口,可以作為線程執行對象 class TicketSeller implements Runnable{ //票數 private static int tickets = 100; //該售票窗口編號 private int code; public TicketSeller(int code) { this.code = code; } @Override public void run() { //如果有票就一直賣 while(tickets > 0) { System.out.println(code + "_____" + tickets--); //賣過票之后休眠一小會等待其他線程操作 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
這段不加鎖的代碼會遇到許多很尷尬的問題,首先一個,多線程之間的重復賣票:
除了重復賣票,還有超額賣票的行為:
這當然是不能容忍的,解決辦法是在賣票過程對tickets
變量加鎖,使得每次只能有一個線程進入賣票的環節而其他線程只能循環等待:
但是這樣處理并不能完全結局上面的問題,盡管每次只能一個線程進入賣票階段阻止了重復賣票。但是超額賣票的行為依舊會發生:
好嘛,這次非常嚴重
原因嗎其實并不復雜,我們加鎖只是能阻止多個進程進入賣票程序,但是會有其他程序達成判斷條件,執行到賣票程序之前等待進入,如果一個線程將票賣完而此時有其他程序剛好等待進入,那么就會出現上面的情況。
所以我們還需要加上一道保險:
經過這樣的處理,票子就可以放心的賣出而不用擔心重或者賣超了
public class Ticket { public static void main(String[] args) { for(int i = 1;i <= 5;i++) { //創建5個線程并啟動他們 //注意一定要使用Thread類創建線程并使用start方法啟動 //而不是直接創建TicketSeller對象調用run方法!!!!!! new Thread(new TicketSeller(i)).start(); } } } //售票類,實現Runnable接口,可以作為線程執行對象 class TicketSeller implements Runnable{ //票數 private static int tickets = 100; //同步鎖 private static Object lock = new Object(); //該售票窗口編號 private int code; public TicketSeller(int code) { this.code = code; } @Override public void run() { //如果有票就一直賣 while(tickets > 0) { synchronized (lock) { //如果票賣完了則跳出 if(tickets <= 0) { break; } System.out.println(code + "_____" + tickets--); //賣過票之后休眠一小會等待其他線程操作 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
在前面我們還提出了另一種方案,就是使用一個對象管理票的售賣。這種方案就不展開啰嗦了,直接上代碼:
public class Ticket { public static void main(String[] args) { //創建一個票管理對象,票數為100 TicketSet ts = new TicketSet(100); //創建5個線程,使用同一個票管理對象 for(int i = 1;i <= 5;i++) { new Thread(new TicketSeller(ts, i)).start(); } } } //票管理類 class TicketSet{ //票數 private int tickets; public TicketSet(int tickets) { this.tickets = tickets; } private boolean hasTicket() { return tickets > 0; } //售票方法,使用同步鎖,每次只能有一個線程訪問該方法 //返回結果為是否賣出去票 synchronized public boolean sellTicket(int code) { if(hasTicket()) { System.out.println(code + "_____" + tickets--); return true; }else { return false; } } } //售票類 class TicketSeller implements Runnable{ //票管理對象 private TicketSet ts; private int code; public TicketSeller(TicketSet ts,int code) { this.ts = ts; this.code = code; } @Override public void run() { //嘗試調用票管理的售票方法,售票成功后休眠一小會 while(ts.sellTicket(code)){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
Java的基本數據類型分為:1、整數類型,用來表示整數的數據類型。2、浮點類型,用來表示小數的數據類型。3、字符類型,字符類型的關鍵字是“char”。4、布爾類型,是表示邏輯值的基本數據類型。
關于怎么在Java中利用多線程模擬站點售票問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。