Big data journey for beginners of strange upgrade < Java object-oriented advanced multithreading safety and wake-up mechanism >

Xiaobai's big data journey (27)

Java object-oriented advanced multithreading security and wake-up mechanism

Last review

In the last issue, we learned the concept of multithreading and the basic use of multithreading. This chapter explains the remaining knowledge points of multithreading, thread safety and solution, and locking mechanism. After learning these knowledge points, multithreading is basically over. Let's start today's content

Thread safety

Thread safety problems and cause analysis

  • In order to meet the demand of summer air conditioning sales peak season, company M requires to produce at least 50 air conditioners every day and hand them to its sales teams T and R for sales. Their task is to complete the sales task of at least 25 air conditioners for each team

T team sales (using Thred to sell air conditioners)

public class Demo3 {
    public static void main(String[] args) {
        // Lao Li from team T came to work
        Thread t1 = new CompanyT("Lao Li");
        // Xiao Zhang from team T came to work
        Thread t2 = new CompanyT("Xiao Zhang");
        // Lao Li from team T went out to sell air conditioners
        t1.start();
        // Xiao Zhang of team T went out to sell air conditioners
        t2.start();

    }
}
// T team
class CompanyT extends Thread{
    // Air conditioning inventory of the current day
    private int airNum = 25;
    // Name of sales team
    private String name;

    // constructor 
    public CompanyT(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        // Sales of the day
        while (airNum>0)
            System.out.println("T team"+name+"Sell an air conditioner"+", Air conditioning surplus of the day:"+ --airNum + "platform");
    }
}

Sales of air conditioners in R team (using Runnable implementation class)

package com.test01Thread;

public class Demo4 {
    public static void main(String[] args) {
        // Runnable instantiation
        Runnable r = new CompanyR();
        // Xiaobai from team R came to work
        Thread groupR1 = new Thread(r,"Xiaobaibai");
        // Xiao Qian from team R came to work
        Thread groupR2 = new Thread(r,"Xiao Qian");
        // Xiaobai of team R went out to sell air conditioners
        groupR1.start();
        // Xiaoqian from team R went out to sell air conditioners
        groupR2.start();

    }
}


class CompanyR implements Runnable{
    // Air conditioning inventory of the current day
    private int airNum = 25;
    
    @Override
    public void run() {
        // Sales of the day
        while (airNum>0)
            System.out.println("R team"+Thread.currentThread().getName()+"Sell an air conditioner"+", Air conditioning surplus of the day:"+ --airNum + "platform");
    }
}

After the end of the day's task, boss Main invited everyone to eat hot pot to celebrate the excellent completion of the task by the two sales teams; And please share your sales experience

After listening to the two teams and seeing the sales list of team members, it's amazing: Why did team T sell 50 air conditioners? Mingming's task on that day is 25 sets for each team, so he plans to raise salary for team T

The members of team r quit and shouted: team T cheated. They copied the sales order of air conditioner. There was so much inventory. How could it be overfulfilled? Boss Main asked: how do you know? Xiao Qian in the R team said that when she made the sales order today, the remaining inventory with Xiao Zhang was 23 units. At that time, she thought she just took it wrong and didn'T pay attention. What is the reason for this problem?

  • The way to implement the Runnable interface is because all threads are created with the same Runnable object, so multiple threads actually perform the same task, which also shares resources
  • By inheriting the Thread, when a new Thread is created and started, a new copy of its bound task is also created. In this way, their tasks are independent of each other, and naturally they cannot realize resource sharing

Summarize the causes of thread safety problems:

  1. Multithreaded execution
  2. shared data
  3. Multiple statement operations share data
  • With the constant quarrel between the two teams, boss Main finally understood the reason: when team T sold air conditioners, because team members Lao Li and Xiao Zhang each took an inventory list when they went out for sales, so each of them sold five air conditioners
  • Now it's troublesome. Boss Main quickly called the factory and asked the workers to work overtime urgently to produce the five missing air conditioners. Although team T overfulfilled the task, the factory workers were unwilling (asking for overtime salary...)

Thread safety problem solution

After the last accident that almost caused the workers to work overtime, boss Main was thinking about a solution. At this time, a young man who called himself synchronized came to apply for the job. He immediately pointed out the causes of the previous problems:

  • Your current business process is not very reliable (thread is not safe). Let me manage the inventory of the whole air conditioner in a unified way. Each air conditioner sold must be approved by me. After one is sold out, you can come to me to apply for sale again. In this way, each air conditioner can only be sold by one salesman, and the problem will be solved naturally
  • The prerequisites 1 and 2 of the above thread safety problems are what we need. We can only find a way to solve them from the third point. To solve the security problem of multi-threaded concurrent access to a resource mentioned above: that is, to solve the problem that duplicate inventory and inventory air conditioning do not exist, Java provides a thread synchronization mechanism to solve it

The keyword synchronized is often used in java to implement the synchronization mechanism:

  • Synchronization method

    public synchronized void method(){
        Code that may cause thread safety problems
    }
    
  • Synchronous code block

    synchronized(Synchronous lock){
         Code requiring synchronous operation
    }
    

Main's boss decided to try synchronized for a day to see if his idea was feasible:

Lock object:

Synchronization lock object:

  • The lock object can be of any type.
  • Multiple thread objects should use the same lock
  • There are two ways to realize code synchronization and block synchronization

Lock object of synchronization code block

  • In static code block: use the Class object of the current Class
  • In non static code blocks: it is customary to consider this first, but pay attention to whether it is the same this
    // Use synchronized in non static code blocks
    package com.test01Thread;
    /*
    Because CompanyT is a subclass of Thread, when instantiated, it is equivalent to reopening a memory space. Therefore, the synchronization lock in synchronized can only use the Class object of the current Class. If This is used, it is equivalent to that each instantiated object has a lock, and multiple statements still operate to share data
    * */
    public class Demo3 {
        public static void main(String[] args) {
            // Lao Li from team T came to work
            Thread t1 = new CompanyT("Lao Li");
            // Xiao Zhang from team T came to work
            Thread t2 = new CompanyT("Xiao Zhang");
    
    
            // Lao Li from team T went out to sell air conditioners
            t1.start();
            // Xiao Zhang of team T went out to sell air conditioners
            t2.start();
    
        }
    }
    // T team
    class CompanyT extends Thread{
        // Air conditioning inventory of the day
        private static int airNum = 25;
        // Name of sales team
        private String name;
    
        // constructor 
        public CompanyT(String name) {
            this.name = name;
        }
    
        @Override
        public void run() {
            // Sales of the day
            while (true) {
                // Add a synchronization lock in the non static code block. At this time, because it is an inherited subclass of Thread, the synchronization lock here must be the Class of the Class
                synchronized (CompanyT.class) {
                    if (airNum>0)
                    System.out.println("T team" + name + "Sell an air conditioner" + ", Air conditioning surplus of the day:" + --airNum + "platform");
                    else
                        break;
                }
            }
        }
    }
    

Lock object of synchronization method

  • Static method: use the Class object of the current Class as the synchronization lock
  • Non static method: this is used as the synchronization lock by default
    // Using synchronous locks in non static methods
    package com.test01Thread;
    /*
    M In order to meet the demand of air conditioning sales in summer, the company requires to produce at least 10 air conditioners every day
     Now you need to hand it over to your sales teams T and R for sales
    * */
    public class Demo3 {
        public static void main(String[] args) {
            // Lao Li from team T came to work
            Thread t1 = new CompanyT("Lao Li");
            // Xiao Zhang from team T came to work
            Thread t2 = new CompanyT("Xiao Zhang");
    
    
            // Lao Li from team T went out to sell air conditioners
            t1.start();
            // Xiao Zhang of team T went out to sell air conditioners
            t2.start();
    
        }
    }
    // T team
    class CompanyT extends Thread{
        // Air conditioning inventory of the day
        private static int airNum = 25;
        // Name of sales team
        private String name;
    
        // constructor 
        public CompanyT(String name) {
            this.name = name;
        }
    
        @Override
        public void run() {
            // use
            saleAir();
        }
    
        // Using synchronous locks in non static methods
        public synchronized void saleAir(){
            // Sales of the day
            while (true) {
                // Add synchronization lock in non static code block
                if (airNum>0)
                    System.out.println("T team" + name + "Sell an air conditioner" + ", Air conditioning surplus of the day:" + --airNum + "platform");
                else
                    break;
            }
        }
    }
    

After a one-day trial, there was no repeated inventory problem. The boss of Main decided to make synchronized regular and increase his salary. The little partners of T and R teams admire and envy, and can't help sighing: knowledge is money. If this sentence is true, it seems that we should learn more in the future

Summary of writing multithreaded programs

  • Multithreading is to improve the running efficiency of the program and reduce the running time of the code. Multithreading is a resource class operated by threads and follows the principle of high cohesion and low coupling
  • The specific implementation steps are to first write resource classes (common resource classes), and then consider thread safety (thread operation resources). For thread safety, you can consider using synchronous code blocks or synchronous methods
  • Generally speaking, when we implement a function, we want to increase its operation efficiency, and it has a public resource that allows us to split this function. Just like M air conditioning company, its whole is the production, sales and installation of air conditioning, and its public resource is air conditioning, so we can split it according to this condition

Thread safety in singleton mode

  • When introducing design patterns in the chapter of internal classes, I introduced two creation methods of singleton patterns: lazy and hungry. At that time, it was said that hungry is caused by thread safety. Today, I will introduce how to solve the thread safety problem of hungry
    public class Singleton {
        private static Singleton ourInstance;
    
        public static Singleton getInstance() {
            //Once the object is created and then obtained again, it will not enter the synchronization code block again to improve efficiency
            if (ourInstance == null) {
                //Synchronization lock, which locks the judgment statement and the statement that creates an object and assigns a value
                synchronized (Singleton.class) {
                    if (ourInstance == null) {
                        ourInstance = new Singleton();
                    }
                }
            }
            return ourInstance;
        }
    
        private Singleton() {
        }
    }
    //Test class
    public class Demo {
        public static void main(String[] args) {
            //Enable multiple threads to obtain a single instance
            new SingletonThread().start();
            new SingletonThread().start();
            new SingletonThread().start();
    
        }
    }
    //Thread class
    class SingletonThread extends Thread{
        @Override
        public void run() {
                Singleton instance = Singleton.getInstance();
                System.out.println(instance);//Print the object address and check whether the instance obtained by each thread is the same
        }
    }
    
  • The thread safety problem of starving Chinese singleton mode is also the internal modification of public resources. Therefore, to solve the starving Chinese singleton mode, you need to add a synchronization lock to the created object and judge again whether a new object is created

Wait for wake-up mechanism

Inter thread communication

  • Still take M air conditioning company as an example. After M company split the whole company's business into three threads, the efficiency has been improved a lot. In the previous thread safety problem, the daily sales tasks of team T and team R need to go to synchronized to obtain the inventory list
  • With an inventory list, the following sales and installation teams can sell and install air conditioners according to the inventory list, which is a common resource
  • Multiple threads are processing the same resource, but the processing actions (tasks of threads) are different. When multiple threads execute concurrently, the CPU switches threads randomly by default. When we need multiple threads to complete a task together, and we want them to execute regularly, then some communication mechanisms are needed between multiple threads to coordinate their work, so as to help us achieve multi threads to operate a piece of data together

Wait for wake-up mechanism

  • The wake-up waiting mechanism is a cooperative mechanism among multiple threads. When it comes to threads, we often think of the race between threads, such as competing for locks, but this is not the whole story. There will also be a cooperation mechanism between threads
  • When a thread meets a certain condition, it will enter the waiting state. It will wake up only after other threads complete the code specified by the thread. You can also set the waiting time to wake up automatically when the time is up
  • When M company produces air conditioners every day, if fewer people buy air conditioners and the salesperson is not very diligent, the air conditioning inventory will be more and more. Similarly, if the salesperson doesn't sell the air conditioner, the installer will have nothing to do. Therefore, we need to wait for the wake-up mechanism to tell the thread to ensure the smooth operation of the whole process

Use wait and wake

  • wait for
    • The wati() thread is no longer active, no longer participates in scheduling, enters the wait set, and no longer competes for locks. It will wait for other threads to perform a special action - notify() wakes it up or enters the scheduling queue again after the waiting time
  • awaken
    • notify() selects a thread in the wait set of the notified object to release
    • notifyAll() releases all threads in the wait set of the notified object

Details of calling 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. The wait method and notify method belong to the methods of Object class. The Object can inherit any class because the Object belongs to any class.
  3. The wait method and notify method must be used in the synchronization code block or synchronization function. Because: these two methods must be called through the lock object

producer consumer problem

  • Waiting for wake-up mechanism can solve the classic problem of "producer and consumer"
  • This is a classic case of a classic multi-threaded synchronization problem, which describes the problems that will occur when two (more) threads sharing a fixed size buffer -- the so-called "producer" and "consumer" -- actually run

There are actually two problems implied in the problem of producers and consumers:

  • Thread safety problem: because producers and consumers share data buffers, but this problem can be solved using synchronization.
  • Coordination of threads:
    • To solve this problem, the producer thread must wait when the buffer is full, pause and enter the blocking state. When the next consumer consumes the data in the buffer, notify the waiting thread to return to the ready state and restart adding data to the buffer. Similarly, you can also let the consumer thread wait when the buffer is empty, pause and enter the blocking state, wait until the producer adds data to the buffer, and then notify the waiting thread to return to the ready state. Such problems can be solved through such a communication mechanism

The problem of a worker and a salesman in M air conditioning company

  • When M air conditioning company just split the process, it only hired one worker to produce and assemble air conditioners, and then the boss of Main sold it himself. Because the starting business has limited places, there is only one two room house, one for the production of air conditioners and one for external sales. Only five air conditioners can be placed in the outside house. Then there is the following situation:
package com.test03WriterandCooker;
/*
* Consumer and producer issues
* */
public class Demo {
    public static void main(String[] args) {
        // public resource 
        WorkRoom workRoom = new WorkRoom();

        // Create workers and start
        new Thread(new Worker(workRoom),"worker").start();
        // Create sales and launch
        new Thread(new Sales(workRoom),"Main boss").start();

    }
}

// worker
class Worker implements Runnable{
    private WorkRoom workroom;

    // Create constructor
    public Worker(WorkRoom workroom) {
        this.workroom = workroom;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(10);//Take a break and zoom in
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // Constantly producing and assembling air conditioners
            workroom.putAir();
        }

    }
}

// sale
class Sales implements Runnable{
    private WorkRoom workroom;

    // Create constructor
    public Sales(WorkRoom workroom) {
        this.workroom = workroom;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(30);//Take a break and zoom in
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // Keep selling air conditioning methods
            workroom.getAir();
        }

    }
}


// Public resource access air-conditioned workshop
class WorkRoom{
    // Maximum number of air conditioners produced
    private int maxAir = 5;
    // Current number of air conditioners
    private int airNum;

    public synchronized void putAir(){
        if (airNum>=maxAir) {
            try {
                this.wait(100);//Wait when the workbench is full
                System.out.println("The workers have a rest....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("The workers produced an air conditioner and stored it in the workshop. At present, the total inventory of air conditioners is surplus: "+ ++airNum + "platform");
        this.notify();//Randomly wakes up a thread waiting on this monitor

    }
    public synchronized void getAir(){
        if (airNum<0){
            try {
                this.wait(10);//Wait after the air conditioner in the studio is full
                System.out.println("Main The boss has a rest....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Main The boss sold an air conditioner and obtained it from the workshop. The current total inventory of air conditioners is surplus: "+ --airNum + "platform");
        this.notify();//Wake up a waiting thread randomly
    }

}

Code interpretation:

  • First, create a public resource class, WorkRoom, which is used to store air-conditioned rooms, and define the maximum number of air-conditioners stored in the room, the current inventory, and the methods of producing and selling air-conditioners
  • Worker, used to produce air conditioner (call public resource class)
  • Sales salesperson class, used to sell air conditioners (call public resource class)
  • Main method to create multithreading
  • In order to solve the problem of thread safety, synchronous lock is used in the method of accessing air conditioner

Release lock operation and deadlock

Operation of releasing lock

  • The execution of the synchronization method and synchronization code block of the current thread ends.
  • The current thread has an unhandled Error or Exception in the synchronization code block and synchronization method, resulting in the abnormal end of the current thread.
  • The current thread executes the wait() method of the lock object in the synchronization code block and synchronization method. The current thread is suspended and the lock is released

Operation that does not release the lock

  • When a thread executes a synchronization code block or synchronization method, the program calls thread sleep(),Thread. The yield () method pauses the execution of the current thread.
  • When a thread executes a synchronization code block, other threads call the thread's suspend() method to suspend the thread, and the thread will not release the lock (synchronization monitor). Try to avoid using stale methods such as suspend() and resume() to control threads

deadlock

  • The same thread locks the synchronization monitor object required by the other party and does not release it. When the other party gives up first, it forms a thread deadlock. Once a deadlock occurs, the whole program will not give any exception or prompt, but all threads are blocked and cannot continue
  • When selling air conditioners, the boss of Main encountered a headache. The customer said to deliver the goods first and then give money. However, due to lack of funds, the boss of Main insisted on giving money first and then delivering the goods, so there was a deadlock
    public class TestDeadLock {
    	public static void main(String[] args) {
    		Object g = new Object();
    		Object m = new Object();
    		Owner s = new Owner(g,m);
    		Customer c = new Customer(g,m);
    		new Thread(s).start();
    		new Thread(c).start();
    	}
    }
    class Owner implements Runnable{
    	private Object goods;
    	private Object money;
    
    	public Owner(Object goods, Object money) {
    		super();
    		this.goods = goods;
    		this.money = money;
    	}
    
    	@Override
    	public void run() {
    		synchronized (goods) {
    			System.out.println("Please pay first.");
    			synchronized (money) {
    				System.out.println("deliver goods");
    			}
    		}
    	}
    }
    class Customer implements Runnable{
    	private Object goods;
    	private Object money;
    
    	public Customer(Object goods, Object money) {
    		super();
    		this.goods = goods;
    		this.money = money;
    	}
    
    	@Override
    	public void run() {
    		synchronized (money) {
    			System.out.println("First delivery");
    			synchronized (goods) {
    				System.out.println("Give me more money");
    			}
    		}
    	}
    }
    

In the end, they couldn't compete... The customer finally came to pick up the goods in person

summary

  • The knowledge of multithreading is over today. In java, multithreading is a difficulty, especially the thread safety and deadlock when operating public resources in multithreading. Because the compilation can pass and the operation does not report errors, it is easy to ignore these problems and cause data or program exceptions. Don't panic when encountering problems. Analyze the causes step by step. If the program is large, use @ Test for step-by-step adjustment. If the program is small, use breakpoint adjustment
  • Well, today's content is these. In the next issue, I will bring you the knowledge points of network programming and review the previous IO and today's multi threading knowledge points according to network programming.

Keywords: Java Programming Big Data Multithreading

Added by nick314 on Sun, 20 Feb 2022 09:58:57 +0200