Java Multithreaded Code Synchronization

Data sharing by multiple threads (multiple threads access the same data together) requires data synchronization to ensure that the same data can only be accessed by one thread at a time.

Synchronization is used to prevent multiple threads from reading and writing the same data at the same time. If the same data is read-only and not modified, it is not necessary to use synchronization.

 

 

Take selling tickets for example. Do not use synchronization

public class SaleTicketThread extends Thread {
    private static int ticket = 100; //Number of votes, static
    
    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println("Sales No." + (100 - ticket + 1) + "Seats");
            ticket--;
        }
    }

}

 

public class SaleTicket {

    public static void main(String[] args) {
        SaleTicketThread thread1 = new SaleTicketThread();
        thread1.start();

        SaleTicketThread thread2 = new SaleTicketThread();
        thread2.start();

        SaleTicketThread thread3 = new SaleTicketThread();
        thread3.start();
    }

}

Start three threads to sell tickets, all accessing the same data SaleTicketThread.ticket Remaining votes

 

Problem prone

 

 

 

 

 

Three common synchronization methods

  • Synchronization method. By default, using the current object (this) or class object of the current class as a lock locks the entire method
  • Synchronize Code Blocks. You can choose what locks to use and only synchronize parts of the code that will cause synchronization problems rather than the entire method, which is more granular than synchronization methods
  • ReentrantLock locks itself, releases locks, and locks only part of the code, more granular than synchronization methods

 

 

1. Synchronization method

public class SaleTicketThread extends Thread {
    private static int ticket = 100; //Number of votes, static

    protected synchronized void saleTicket(){ //use synchronized Modification Method
        while (ticket > 0) { 
            System.out.println("Sales No." + (100 - ticket + 1) + "Seats");
            ticket--;
        }
    }
    
    @Override
    public void run() {
        saleTicket();
    }

}

If the method is not decorated with static, it is an instance method, with this (the current object) as the lock by default;

If the method is static, the class object of the current class is the default lock.

 

 

 

2. Synchronize Code Blocks

public class SaleTicketThread extends Thread {
    private static Integer ticket = 100; //Number of votes, static

    @Override
    public void run() {
        synchronized (ticket){ //If the lock is Object type
            while (ticket > 0) {
                System.out.println("Sales No." + (100 - ticket + 1) + "Seats");
                ticket--;
            }
        }
    }

}

Synchronization code blocks can decide which lock object to use, either directly using shared data as a lock or creating a new Object object as a lock.

 

Two points to note:

1. If a lock is of Object type, the basic types such as int, float, char do not inherit Object and cannot be used as a lock. Instead, use the corresponding wrapper type.String inherits Object and can act as a lock.

2. Threads accessing this shared data should use the same lock objects (same object addresses).

 

 

 

3,ReentrantLock

public class SaleTicketThread extends Thread {
    private static Integer ticket = 100; //Number of votes, static
    private final static ReentrantLock lock=new ReentrantLock(); //Lock object, final static Modification,

    @Override
    public void run() {
        lock.lock(); //Locking
        while (ticket > 0) {
            System.out.println("Sales No." + (100 - ticket + 1) + "Seats");
            ticket--;
        }
        lock.unlock(); //Release lock
    }

}

With the ReentrantLock object as the lock, note that all threads using this shared data should use the same ReentrantLock object (if the same lock object).

Actually, similar to synchronization code block, lock() marks the beginning of synchronization code block and unlock() marks the end of synchronization code block.

 

 

Exceptions may occur:

public class XxxThread extends Thread {
    private final static ReentrantLock lock=new ReentrantLock(); //Lock object, final static Modification

    @Override
    public void run() {
        //...
        
        lock.lock(); //Locking
        try{
            //......  //Place in try in
        }catch ( ){
            //.....
        }finally {
            lock.unlock(); //stay finally Medium release lock
        }

        //.....
    }

}

 

 

 

 

Explain

Shared data and locks can be placed in a thread as static variables.

If you want to use this member variable in different thread classes, such as Query and Ticketing, then declare it public static and expose it.

If it is used only in one thread class (multiple instances of this thread class share this data), set it to private static.

 

You can also place shared data and locks in parent threads (the threads that open them) and pass in shared data and lock objects in the constructor of a thread class.

 

In summary, all threads accessing the shared data should have access to the shared data and locks.

 

All threads accessing this shared data use the same lock object (the same object address).

Tags: Java

Posted on Sun, 22 Mar 2020 00:58:57 -0400 by petebolduc