Multithreaded classic case: multithreaded ticket

We define a thread class for ticketing that acts as a conductor

  • Define a static variable to act as a pool of public tickets and record the number of tickets
  • Implement the functional logic of selling tickets in the run method

Ticket thread class:

public class TicketThread extends Thread{
    // Public ticket pool
    private static int ticketNum = 100;

    @Override
    public void run(a) {
        while (ticketNum>0) {if(ticketNum>0){
                System.out.println(Thread.currentThread().getName() + "Sold ticket number"+ ticketNum); ticketNum--; }}}}Copy the code

Create multiple threads to sell tickets:

public static void main(String[] args) {
    TicketThread t1 = new TicketThread();
    TicketThread t2 = new TicketThread();
    TicketThread t3 = new TicketThread();
    t1.setName("Conductor 1");
    t2.setName("Conductor 2");
    t3.setName("Conductor 3");
    t1.start();
    t2.start();
    t3.start();
}
Copy the code

Results:

If we run it multiple times, we’ll see that different threads are selling the same ticket over and over again, why is that, because there’s a thread safety issue


Multithreading – Thread safety issues

Thread safety: Multiple threads accessing the same data may cause thread safety problems

Thread safety in Java:

  • synchronous
  • lock

synchronously

The Synchronized keyword in Java modifies code blocks and methods

Synchronizing code blocks:

  • Using a synchronized block requires a listener
  • To ensure that all threads share a synchronized listener, that is, the synchronized listener is shared by all threads, such as static properties, bytecode file objects, etc
public class TicketThread extends Thread{
    // Public ticket pool
    private static int ticketNum = 100;

    @Override
    public void run(a) {
        while (ticketNum>0) {// Synchronize code blocks
            synchronized (TicketThread.class){
                if(ticketNum>0){
                    System.out.println(Thread.currentThread().getName() + "Sold ticket number" + ticketNum);
                    ticketNum--;
                }
            }
        }
    }
}
Copy the code

Synchronization method:

  • Synchronized is the keyword synchronized above the methods that need to be synchronized
  • Position of addition: before the return value type
  • Write synchronization listeners that are not required and cannot be displayed
  • If it is a non-static method, the synchronization listener is this
  • If the method is static, the sync listener is the bytecode object of the class in which the method is currently being used
public class TicketThread extends Thread{
    // Public ticket pool
    private static int ticketNum = 100;

    @Override
    public void run(a) {
        while (ticketNum>0){ test(); }}/** * sync method */
    static synchronized void test(a){
        if(ticketNum>0){
            System.out.println(Thread.currentThread().getName() + "Sold ticket number"+ ticketNum); ticketNum--; }}}Copy the code

Lock mode

  • Create an implementation class object for the Lock interface (Lock object)
  • Locking in core code where thread safety may occurlock()methods
  • Using a try… catch… Finally code block, finally unlockedunlock()methods

Note: Locking is more flexible and efficient than synchronization. Synchronization is maintained by the JVM. Locking code level requires manual release

public class TicketThread extends Thread{
    // Public ticket pool
    private static int ticketNum = 50;
    // Create lock object, static modifier, shared by all ticket-selling threads
    static Lock lock = new ReentrantLock();
    @Override
    public void run(a) {
        while (ticketNum>0) {/ / lock
            lock.lock();
            try {
                if(ticketNum>0){
                    System.out.println(Thread.currentThread().getName() + "Sold ticket number"+ ticketNum); ticketNum--; }}catch (Exception e) {
                e.printStackTrace();
            }finally {
                / / unlocklock.unlock(); }}}}Copy the code