Using Java's lock mechanism to realize the case of multi-threaded ticket sales

This article was first published from "MOOC.com". If you want to know more about IT dry goods and hot news in the programmer circle, please pay attention to "MOOC.com" and "MOOC.com Official Account"!

Author: Wang Junwei Tech | MOOC Lecturer


1 Introduction

The content of this article is mainly to use the lock mechanism of Java to implement the case of multi-threaded ticket sales. In most cases, the case of ticket sales mainly focuses on how multi-threading can safely reduce inventory, that is, the remaining number of tickets. When the number of tickets is 0, stop reducing inventory.

In addition to focusing on the reduction of ticket inventory, the content of this article will also involve the refund window, which can more closely simulate the real scene.

This paper requires learners to focus on the following two points:

  • Master the multi-threaded ticketing mechanism model, and if similar scenes are involved in the follow-up work, you can understand the overall structure of the scene at the first time;
  • Use Condition and Lock to implement ticketing mechanism.

2. Ticketing Mechanism Model

The ticketing mechanism model is derived from real-life ticketing scenarios, from the initial single-window ticket sales to multi-window ticket sales, from the initial manual ticket counting to the subsequent system intelligent online ticket sales. Multi-concurrent programming can realize this ticketing scenario, and ensure the safety of threads and the correctness of ticket numbers in the case of multi-window ticketing.

 

As shown in the figure above, there are two ticket windows for ticket sales, and one window for ticket refunds. This is a simple ticket sales mechanism in real life.

3. Implementation of the ticketing mechanism

Scene design :

  • Create a factory class TicketCenter, which contains two methods, saleRollback refund method and sale ticket sales method;
  • Define a total number of tickets equal to 10, for the convenience of observing the results, set it to 10. Learners can also choose the number by themselves;
  • For the saleRollback method, when a refund occurs, notify the ticket window to continue selling tickets;
  • Make special settings for saleRollback, and return a ticket every 5000 milliseconds;
  • For the sale method, as long as there are tickets, they will be sold. In order to make it easier to observe the results, sleep for 2000 milliseconds for each ticket sold;
  • Create a test class, create 2 ticket windows and 1 refund window in the main function, and run the program to observe the results.
  • Modify the refund time of saleRollback, and return a ticket every 25 seconds;
  • Run the program again and observe the results.

Implementation requirements : This experiment requires the use of ReentrantLock and Condition interfaces to implement a synchronization mechanism.

Example :

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; // 根据需求:定义10涨车票
    private Lock lock = new ReentrantLock(false);
    private Condition saleLock = lock.newCondition();
    // 根据需求:saleRollback 方法创建,为退票使用
    public void saleRollback() {
        try {
            lock.lock();
            capacity++;
            System.out.println("线程("+Thread.currentThread().getName() + ")发生退票。" + "当前剩余票数"+capacity+"个");
            saleLock.signalAll(); //发生退票,通知售票窗口进行售票
        } finally {
            lock.unlock();
        }
    }

    // 根据需求:sale 方法创建
    public void sale() {
        try {
            lock.lock();
            while (capacity==0) { //没有票的情况下,停止售票
                try {
                    System.out.println("警告:线程("+Thread.currentThread().getName() + ")准备售票,但当前没有剩余车票");
                    saleLock.await(); //剩余票数为 0 ,无法售卖,进入 wait
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            capacity-- ; //如果有票,则售卖 -1
            System.out.println("线程("+Thread.currentThread().getName() + ")售出一张票。" + "当前剩余票数"+capacity+"个");
        } finally {
            lock.unlock();
        }
    }
}

class saleRollback implements Runnable {
    private TicketCenter TicketCenter; //关联工厂类,调用 saleRollback 方法
    public saleRollback(TicketCenter TicketCenter) {
        this.TicketCenter = TicketCenter;
    }
    public void run() {
        while (true) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            TicketCenter.saleRollback(); //根据需求 ,调用 TicketCenter 的 saleRollback 方法

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

Result verification :

线程(1号售票窗口)售出一张票。当前剩余票数9个
线程(2号售票窗口)售出一张票。当前剩余票数8个
线程(2号售票窗口)售出一张票。当前剩余票数7个
线程(1号售票窗口)售出一张票。当前剩余票数6个
线程(1号售票窗口)售出一张票。当前剩余票数5个
线程(2号售票窗口)售出一张票。当前剩余票数4个
线程(退票窗口)发生退票。当前剩余票数5个
线程(1号售票窗口)售出一张票。当前剩余票数4个
线程(2号售票窗口)售出一张票。当前剩余票数3个
线程(2号售票窗口)售出一张票。当前剩余票数2个
线程(1号售票窗口)售出一张票。当前剩余票数1个
线程(退票窗口)发生退票。当前剩余票数2个
线程(1号售票窗口)售出一张票。当前剩余票数1个
线程(2号售票窗口)售出一张票。当前剩余票数0个
警告:线程(1号售票窗口)准备售票,但当前没有剩余车票
警告:线程(2号售票窗口)准备售票,但当前没有剩余车票
线程(退票窗口)发生退票。当前剩余票数1个
线程(1号售票窗口)售出一张票。当前剩余票数0个
警告:线程(2号售票窗口)准备售票,但当前没有剩余车票
警告:线程(1号售票窗口)准备售票,但当前没有剩余车票

Result analysis : From the results, we have correctly completed the mechanism of ticket sales and refund, and used the ReentrantLock and Condition interfaces.

Code Fragment Analysis 1 : Look at the ticketing method code.

public void sale() {
        try {
            lock.lock();
            while (capacity==0) { //没有票的情况下,停止售票
                try {
                    System.out.println("警告:线程("+Thread.currentThread().getName() + ")准备售票,但当前没有剩余车票");
                    saleLock.await(); //剩余票数为 0 ,无法售卖,进入 wait
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            capacity-- ; //如果有票,则售卖 -1
            System.out.println("线程("+Thread.currentThread().getName() + ")售出一张票。" + "当前剩余票数"+capacity+"个");
        } finally {
            lock.unlock();
        }
    }

The main point of view is that only the await method is used in the method, because the refund is triggered by the scene, the ticket window does not need to wake up the refund window, because in the real scene, there may be no refund, so there is no need to wake up. This is quite different from the producer and consumer model.

Code Fragment Analysis 2 : Look at the refund method code.

public void saleRollback() {
        try {
            lock.lock();
            capacity++;
            System.out.println("线程("+Thread.currentThread().getName() + ")发生退票。" + "当前剩余票数"+capacity+"个");
            saleLock.signalAll(); //发生退票,通知售票窗口进行售票
        } finally {
            lock.unlock();
        }
    }

The refund method only has the signalAll method, which notifies the ticket window to sell tickets without calling the await method, because as long as there is a refund, the ticket can continue to be sold, and there is no definition of the upper limit of inventory, which is also a major difference from the producer and consumer model.

Summary : There are subtle differences between the ticketing mechanism and the producer-consumer model, which requires learners to experience slowly through the implementation of code. Since the ticket sales method only needs to enter the await state, and the ticket refund method needs to wake up the ticket sales await state, so only one Condition object of the ticket window needs to be created.

4. Summary

The content of this article mainly explains the ticketing mechanism model, and the core content is the realization of the ticketing mechanism. The implementation process uses the ReentrantLock and Condition interfaces to realize the synchronization mechanism, which is also the key knowledge of this course.


Welcome to pay attention to the official account of "MOOC". We will always insist on providing high-quality content in the IT circle, sharing dry knowledge, and let's grow together!

This article was originally published on Muke.com, please indicate the source for reprinting, thank you for your cooperation

Guess you like

Origin blog.csdn.net/mukewangguanfang/article/details/130883034