[beginners learn java] D36 thread introduction, thread synchronization mechanism and thread waiting and wake-up mechanism

πŸ§‘πŸ’›πŸ’šπŸ’™πŸ’œπŸ€ŽπŸ’—πŸ§‘πŸ’›πŸ’šπŸ’™πŸ’œπŸ€ŽπŸ’—
Thank you for your support and encouragement. It's not easy to make
πŸ™ Ask for some praise πŸ‘ βž• Collection ⭐ βž• follow βœ…
Get up with one button three times!
πŸ§‘πŸ’›πŸ’šπŸ’™πŸ’œπŸ€ŽπŸ’— πŸ§‘πŸ’›πŸ’šπŸ’™πŸ’œπŸ€ŽπŸ’—

1, Overview of thread safety

If there are multiple threads running at the same time, and these threads may run the same piece of code at the same time. Each time the program runs, the result is the same as that of a single thread, and the values of other variables are the same as expected, so it is thread safe.

2, Thread synchronization mechanism

When we use multiple threads to access the same resource, and multiple threads have write operations on the resource, thread safety problems are easy to occur. To solve the security problem of concurrent access to a resource by multiple processes, Java provides a synchronized mechanism to solve it.

There are three methods to realize synchronous operation in Java: synchronous code block, synchronous method and locking mechanism.

Case, using multithreading to simulate cinema ticket sales, selling 100 tickets, with three ticket windows

//Multithreading is realized by implementing the Runnable interface
public class RunnableImp implements Runnable {
    //Set the total number of votes, 100 tickets
    private int ticket=100;
    //Rewrite the run method, set the thread task, and sell tickets
    @Override
    public void run() {
    
        //Dead cycle, repeat ticket sales
        while (true){
            //Check whether the tickets are sold out
            if (ticket>0){
                //Not sold out. Keep selling tickets
                System.out.println(Thread.currentThread().getName()+"--Selling the third"+ticket+"Ticket");
                ticket--;
            }
        }
    }
}
//Simulated ticket selling
public class test {
    public static void main(String[] args) {
        RunnableImp runnableImp = new RunnableImp();
        Thread thread = new Thread(runnableImp);
        thread.start();//Window 1
        new Thread(runnableImp).start();//Window 2
        new Thread(runnableImp).start();//Window 3

    }
}

Test results:

Thread safety issue 1: sell the same ticket
Thread safety issue 2: selling tickets that don't exist

1. Synchronous code block

Use format:
  synchronize(Lock object){
		//Thread safe code blocks may occur
	}

be careful: 
	1. The lock object can be any data type
	2. Multiple thread objects should use the same lock

Function of lock object:
	Lock the synchronization code block and let only one thread execute in the synchronization code block
	

Sample code;

//Multithreading is realized by implementing the Runnable interface
public  class RunnableImp implements Runnable {
    //Set the total number of votes, 100 tickets
    private int ticket=100;

    //Create an object
    Object obj=new Object();

    //Rewrite the run method, set the thread task, and sell tickets
    @Override
    public void run() {
        //Dead cycle, repeat ticket sales
        while (true){
            //Set synchronization code block
            synchronized (obj){
                //Check whether the tickets are sold out
                if (ticket>0){
                    //Not sold out. Keep selling tickets
                    System.out.println(Thread.currentThread().getName()+"--Selling the third"+ticket+"Ticket");
                    ticket--;
                }
            }
        }
    }
}

Run the test class to get the test results:

From the test results, it can be found that the thread safety problem of selling duplicate tickets and non-existent tickets is solved.

Principle analysis of synchronization technology realized by synchronous code block:

A lock object is used in the synchronization code block, which is also called ("synchronization lock", "object lock" and "object monitor"). In the case of multi-threaded ticket selling, three threads seize the execution right of the CPU together. Which thread grabs the execution right of the CPU will execute the run() method. When the synchronized code block is encountered, It will check whether there is a lock object in the code block. If so, it will obtain the object and enter the code block for execution; If the remaining threads also grab the execution right of the CPU, they will also execute the synchronized synchronization code block and judge whether there is a lock object. If no lock object is found, the thread will enter the blocking state until the thread that obtains the lock object returns the lock object, That is, the thread cannot enter the synchronization and execute the code until the previous thread executes the code in the synchronization code block and returns the lock object to the synchronization code block.
Therefore, the core of synchronization technology is that the thread in synchronization will not release the lock object until it is executed; If the thread outside the synchronization has no lock, the object cannot be synchronized

2. Synchronization method

Two solutions to thread safety:Use synchronization method
 effect:
	The synchronization method will lock the code inside the method and let only one thread use it.
	
Use steps:
1.Extract the code that accesses the shared data, that is, the code that may have thread safety problems,
	Put it in a way
2.Add to method synchronized Modified hunting
  Use format:
	Permission modifier synchronized Return value type method name(parameter list){
		Code that may cause thread safety problems(Code block that accessed the shared data)
}

Example code:

//Multithreading is realized by implementing the Runnable interface
public  class RunnableImp implements Runnable {
    //Set the total number of votes, 100 tickets
    private int ticket=100;

    //Rewrite the run method, set the thread task, and sell tickets
    @Override
    public void run() {
        //Dead cycle, repeat ticket sales
        while (true){
            sellticket();
        }
    }
    //Use synchronization method
    public  synchronized void sellticket(){
        //Check whether the tickets are sold out
        if (ticket>0){
            //Not sold out. Keep selling tickets
            System.out.println(Thread.currentThread().getName()+"--Selling the third"+ticket+"Ticket");
            ticket--;
        }
    }
}

Run the test code: the test result is the same as the test result of the synchronization code block. There are no duplicate tickets or nonexistent tickets

3. Locking mechanism

        java. util. concurrent. locks. The Lock mechanism provides a wider range of locking operations than synchronized code blocks and synchronized methods, and both synchronized code blocks and synchronized methods have the functions of Lock, and Lock can better reflect object-oriented.
Lock lock is also called synchronous lock. Its locking and releasing have been methodized:

	public void lock():   Add synchronous lock.
	public void unlock(:  Release the synchronization lock.

Example code:

Two solutions to thread safety:use Lock lock

Use steps:
	1.Create one at the member location ReentrantLock object
	2.Call before code that may have security problems. Lock Methods in interfaces Lock Acquire lock
	3.Call after code that may have security problems. Lock Methods in interfaces unLock Release lock

//Multithreading is realized by implementing the Runnable interface
public  class RunnableImp implements Runnable {
    //Set the total number of votes, 100 tickets
    private int ticket=100;

    //1. Create a ReentrantLock object at the member location
    Lock lock = new ReentrantLock();
    
    //Rewrite the run method, set the thread task, and sell tickets
    @Override
    public void run() {
        //Dead cycle, repeat ticket sales
        while (true){
            //2. call the Lock in the Lock interface before getting code for security problems.
            lock.lock();
            //Check whether the tickets are sold out
            if (ticket>0){
                //Not sold out. Keep selling tickets
                System.out.println(Thread.currentThread().getName()+"--Selling the third"+ticket+"Ticket");
                ticket--;
            }
            //3. call the method unLock release lock in the Lock interface after the code that may have security problems.
            lock.unlock();
        }
    }
}

Run the test code: the test result is the same as the test result of the synchronization code block. There are no duplicate tickets or nonexistent tickets

3, Six states of threads

  • New (new status)

    • The state when the thread was just created, but the thread has not been started and the start method has not been called.
  • Runnable

    • The state in which a thread can run in the java virtual machine. The thread may or may not be running its own code, depending on the processor of the operating system.
  • Blocked (lock blocked)
    • When a thread attempts to obtain an object lock, but the object lock has been held by other threads, the thread will enter the Blocked blocking state; When the thread holds the lock object, the thread will change to Runnable runnable state.
  • Waiting (infinite waiting state)
    • When a money process is Waiting for another thread to perform a (wake-up) action, the thread enters the Waiting infinite Waiting state. After entering this state, the thread cannot be awakened automatically. It must wait for another thread to call the notify() or notifyAll() method before it can be called.
  • Timed waiting
    • Similar to the waiting infinite wait state, when methods with timeout parameters are called, they will enter the Timed Waiting state. This state will remain until the timeout period expires or a wake-up notification is received. A common method with timeout parameters is thread Sleep() and object wait().
  • Terminated (dead state)
    • The thread that has exited is in this state.

4, Thread waiting and wake-up mechanism

Method used in waiting for wake-up

The wake-up waiting mechanism is used to solve the problem of inter thread communication,

The three methods used are as follows:

  • 1.wait() method: when the wait() method is called, the thread will no longer participate in scheduling and enter the wait set, which will not waste CPU resources or compete for locks. This thread state is waiting. At the same time, it will wait for other threads to perform special actions - > notify that the thread waiting on this object will be released from the walt set and re entered into the ready queue
  • 2.notify() method: select a thread in the wait set of the notified object and release it. That is, wake up a single thread waiting on this monitor
  • 3.notifyAll(): release all threads in the wait set of the notified object. That is, wake up all threads waiting on this monitor

Note: the notified thread cannot enter the execution state immediately because it was interrupted in the synchronization block before. At this time, the thread has no lock object, so it needs to obtain the lock object again. At this time, other threads may compete with it, The thread competes successfully. i will get the lock object before resuming execution before calling to wait() method.
Therefore, it can be concluded that if the thread can obtain the lock, the thread will change from waiting state to running state; Otherwise, the thread just comes out of the wait set and enters the entry set, and the thread will transition from the waiting state to the blocking state again

Details of calling the wait() and notify() methods

  • 1. The wait method and notify method must be called by the same lock object. Because the corresponding lock object can wake up the thread after using the wait() method called by the same lock object through notify.
  • 2. Both the wait () method and the notify() method are methods of the Object class. Because the lock Object can be any type of Object, and the class to which the Object of any class belongs inherits the Object class. I
  • 3.walt() and notlfy() methods must be used in synchronous code blocks or synchronous methods. Because to call these two methods, you must lock the object.

Case: when a customer goes to a beef noodle shop to eat beef noodles, the noodle shop owner is first waiting for the customer to order noodles. When a customer orders a bowl of beef noodles, the boss starts making noodles, and the customer enters the waiting. When the boss's noodles are ready, he will tell the customer that the noodles are ready and the customer starts eating noodles.

Example code:

//Create beef noodle class,
 class  BeefNoodles{
     //Types of noodle soup: Golden soup, red soup,
    String tang;
    //Taste: not spicy, generally spicy
    String kouwei;
    //Face status, with face true and without face false
    boolean flag =false;
}
//Create beef noodle restaurant class thread
public class BeefNoodlesRestaurant extends Thread {
    //Create a beef noodle class, beef noodle variable
    private BeefNoodles beefNoodles;

    //Assign a value to the beef noodle variable using the parametric construction method
    public BeefNoodlesRestaurant(BeefNoodles beefnoodles) {
        this.beefNoodles = beefnoodles;
    }
    //Rewrite the run method and set the thread task: making beef noodles
    @Override
    public void run() {
        //Define variables to determine the taste of beef noodles,
        int count = 2;//The default golden soup is not spicy
        while (true){//Dead cycle, always making beef noodles
            //Use synchronized code blocks to protect threads
            synchronized (beefNoodles){
                //Judge the status of beef noodles and decide whether to enter the thread waiting state
                if (beefNoodles.flag==true){
                    //Beef noodles already exist (ready), beefNoodles calls the wait() method to enter the wait
                    try{
                        beefNoodles.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //Wake up the thread and make beef noodles
                //Set the taste of beef noodles
               if (count%2==0){
                    beefNoodles.tang="Golden soup";
                    beefNoodles.kouwei="Not spicy";
               }else if (count%3==0){
                    beefNoodles.tang="Red soup";
                    beefNoodles.kouwei="Not spicy";
               }else if (count%5==0){
                    beefNoodles.tang="Golden soup";
                    beefNoodles.kouwei="Generally spicy";
               }else if (count%7==0){
                    beefNoodles.tang="Red soup";
                    beefNoodles.kouwei="Generally spicy";
               }
                //Confirm the type of beef noodles
                System.out.println("Doing"+beefNoodles.tang+beefNoodles.kouwei+"Beef noodles");
                //It takes 6 seconds to make beef noodles
                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //The beef noodle shop made beef noodles
                beefNoodles.flag=true;//Sets the state of the face
                count++;
                //Wake up customers to eat noodles
                beefNoodles.notify();
                System.out.println("objective"+beefNoodles.tang+beefNoodles.kouwei+"Beef noodles have been made");
            }
        }
    }
}
//Create client class thread
public class KeHu extends Thread {
    //Create a beef noodle class, beef noodle variable
    private BeefNoodles beefNoodles;

    //Assign a value to the beef noodle variable using the parametric construction method
    public KeHu(BeefNoodles beefnoodles) {
        this.beefNoodles = beefnoodles;
    }
    //Rewrite the run method and set the thread task: eating beef noodles
    @Override
    public void run() {
        int i=1;//Customer serial number
        while (true){//Dead cycle, always eating beef noodles
            //Use synchronized code blocks to protect threads
            synchronized (beefNoodles){
                //Judge the status of beef noodles and decide whether to enter the thread waiting state
                if (beefNoodles.flag==false){
                    //Beef noodles do not exist (not ready), beefNoodles calls the wait() method to enter the wait
                    try {
                        beefNoodles.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //The state of execution after a thread wakes up
                System.out.println("customer"+Thread.currentThread().getName()+"-->"+i+"I am eating"
                        +beefNoodles.tang+beefNoodles.kouwei+"Beef noodles");
                //The customer finished the beef noodles and modified the status of the noodles
                beefNoodles.flag =false;
                //When the noodles are finished, wake up the noodle shop to make noodles
                beefNoodles.notify();
                System.out.println("customer"+Thread.currentThread().getName()+"-->"+i+"It's finished"
                        +beefNoodles.tang+beefNoodles.kouwei+"Beef noodles"+",The noodle shop continued to make beef noodles");
                i++;//Next customer
                System.out.println("----------------------------------");
            }
        }

    }
}
//Test class
public class Test {
    public static void main(String[] args) {
        //Create a beef face object
        BeefNoodles beefNoodles = new BeefNoodles();
        //Open the thread of beef noodle restaurant to make noodles
        new BeefNoodlesRestaurant(beefNoodles).start();
        //Start the customer thread and the customer starts eating noodles
        new KeHu(beefNoodles).start();
    }
}

Test results:

Keywords: Java

Added by traffic on Sun, 26 Dec 2021 22:52:08 +0200