1、售票任務類:
package com.joyupx.cms.example.globalLock5;
import cn.hutool.core.date.DateTime;
import lombok.extern.slf4j.Slf4j;
import static java.lang.Thread.sleep;
/**
* 售票任務
* @author hapday
* @since 0.0.1
*/
@Slf4j
public class SellTicketTask6 implements Runnable {
// 餘票數
private int ticketsLeft = 50;
// 票號,用來表示已賣了多少張票。
private int ticketNumber = 1;
/**
* 建立【鎖標識】物件,用在全域性鎖中,是多個執行緒爭搶的物件。
* 誰搶到了它就意味著誰擁有的主動權,具體到這裡就是本輪執行任務的權利,繼續處在【執行中(running)】狀態。
* 沒有搶到【鎖標識】的執行緒被迫進入到【阻塞中(blocking)】狀態,等待持有鎖的執行緒釋放了鎖後繼續搶【鎖標識】。
*/
private LockFlag lockFlag = new LockFlag();
/**
* 自定義類實現【可執行介面(Runnable)】介面後必須實現其【執行(run)】方法
* 並且【執行(run)】方法是自動執行的,具體來說是由虛擬機器來主動呼叫的。
*/
@Override
public void run() {
/**
* 當還有餘票時
*/
while (0 < ticketsLeft) {
try {
/**
* 用以模擬每售出一張票後的客戶切換。
*/
sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
/**
* 全域性鎖
* 以自定義物件做為鎖,多個執行緒同時搶它。
*/
synchronized (lockFlag) {
/**
* 防止一票多賣、防止多出票、防止漏票。
*/
if (0 < ticketsLeft) {
log.info("{} 正在出售第 {} 張車票。", Thread.currentThread().getName(), ticketNumber);
// 每賣出一張票,餘票數減一,直至餘票數為零時為止。
ticketsLeft--;
// 每賣出一張票,票號加一。
ticketNumber++;
/**
* 每賣 5 張票做一個分割。
*/
if (ticketNumber % 5 == 1) {
System.out.println("\t" + DateTime.now());
}
}
}
}
}
}
/**
* 鎖標識
* 多個執行緒搶此標識,誰搶到了就表示誰獲得了鎖或持有了鎖,就可以繼續執行,沒有搶到鎖標識的則被迫進入到阻塞狀態
* ,等待持有鎖的執行緒釋放了鎖後再次加入到搶鎖標識的佇列中。
*/
class LockFlag {}
2、售票執行緒:
package com.joyupx.cms.example.globalLock5;
/**
* 測試視窗賣票
*/
public class SellTicketThread {
public static void main(String[] args) {
/**
* 建立售票任務物件
*/
SellTicketTask6 sellTicketTask = new SellTicketTask6();
/**
* 建立 5 個執行緒,模擬 5 個視窗的工作。
*/
/**
* 模擬視窗一的工作
*/
Thread sellTicketThread1 = new Thread(sellTicketTask, "視窗一");
/**
* 模擬視窗二的工作
*/
Thread sellTicketThread2 = new Thread(sellTicketTask, "視窗二");
/**
* 模擬視窗三的工作
*/
Thread sellTicketThread3 = new Thread(sellTicketTask, "視窗三");
/**
* 模擬視窗四的工作
*/
Thread sellTicketThread4 = new Thread(sellTicketTask, "視窗四");
/**
* 模擬視窗五的工作
*/
Thread sellTicketThread5 = new Thread(sellTicketTask, "視窗五");
/**
* 啟動執行緒,模擬視窗開始工作。
* 只有透過啟動(start)方法才能觸發執行(run)方法。
*/
/**
* 啟動執行緒一,模擬視窗一開始工作。
*/
sellTicketThread1.start();
/**
* 啟動執行緒二,模擬視窗二開始工作。
*/
sellTicketThread2.start();
/**
* 啟動執行緒三,模擬視窗三開始工作。
*/
sellTicketThread3.start();
/**
* 啟動執行緒四,模擬視窗四開始工作。
*/
sellTicketThread4.start();
/**
* 啟動執行緒五,模擬視窗五開始工作。
*/
sellTicketThread5.start();
}
}