java multithreading Foundation

1, Thread and process, thread implementation

What is a thread?

To talk about threads, we need to know what a process is (a process contains threads. A process can have multiple threads. When the process ends, the thread must end, the thread ends, and the process does not necessarily end)
Process: it is a program currently being executed. For example, we run qq and wechat at the same time. These two programs are two independent processes

  • A process is a static execution process
  • Occupy specific memory space
  • Different processes are independent of each other, which is composed of cpu, memory address code and memory space data
  • A single core cpu can only process one process at a time. If there are too many processes, the cpu burden will be heavy, so there are many 4-core and 8-core CPUs to process multiple processes at the same time

How does the cpu handle processes

  • At the same time, a single core cpu can only process one process. In short, a process has three states (just in short), ready run wait (blocking)
  • The process prepares all resources except cpu and enters the ready state
  • At this time, when the cpu is idle, it will process the process, and the process is running
  • When this process suddenly needs some resources, such as an io device (this device is very slow, which is not the same order of magnitude as the cpu speed), then the cpu will not wait for this process to obtain the resources it needs, but instead deal with other ready processes
  • At this time, the process enters the waiting state. When it obtains the resources it needs, it is only short of cpu, so it enters the ready state again

Thread: thread is a single controllable process that can be processed continuously. It can be called a lightweight process. A process can have multiple threads

  • A process can have multiple parallel threads
  • All threads of the same process share the same memory unit, memory address space, and can share resources (variables and objects)
  • All threads of the same process allocate objects and communication from the same heap (don't understand the concept of Baidu 'stack'), and can also exchange data and synchronize operations
  • No additional communication mechanism is needed, the communication is simple and the data transmission is fast. (because all threads in a process communicate in the same address space)
  • This is why there are 4-core 8-thread CPUs and 8-core 16 thread CPUs

Thread execution state or life cycle (note that it is a java thread, not a process. The following two pictures are taken from the courseware of Ma soldier Education)

1. How to implement thread 1

package com.company;

/**
 * The first method of implementing threads is implemented through classes
 * 1,Inherit Thread class
 * 2,Implement the run() method
 * 3,Create class objects and call thread methods, such as start() to start threads
 */
public class ThreadTest extends Thread{ //1. Inherit Thread class

    //2. Implement run method
    @Override
    public void run() {
        for (int i = 0;i <= 10;i++){
            Thread.currentThread().setName("ThreadTest Thread of class");//Set the name of the current thread. It can not be set. There is a default value of Thread-0
            System.out.println(Thread.currentThread().getName()+""+i);//Get the current thread name
        }
    }
}

package com.company;

/**
 * The first method of implementing threads is implemented through classes
 * 1,Inherit Thread class
 * 2,Implement the run() method
 * 3,Create class objects and call thread methods, such as start() to start threads
 */
public class Main extends Thread{ //1. Inherit Thread class

    //2. Implement run method
    @Override
    public void run() {
        for (int i = 0;i <= 10;i++){
            Thread.currentThread().setName("Main Thread of class");//Set the name of the current thread. It can not be set. There is a default value of Thread-0
            System.out.println(Thread.currentThread().getName()+""+i);//Get the current thread name
        }
    }

    public static void main(String[] args) {

        //3. Creating a class object can be omitted, because the main method here is in this class, and it cannot be omitted in other cases
        ThreadTest threadTest = new ThreadTest();//Create the first thread class object
	    Main main = new Main();//Create a second

        //4. Thread class to start
        threadTest.start();//Start the first thread
        main.start();//Start the second thread
    }

}

2. How to implement threads 2

package com.company;

/**
 * The second way to implement threads is to inherit the Runnable interface (because the Thread class is implemented by implementing the Runnable interface)
 *      This method is applicable when threads need to be used and other classes need to be inherited (because a class can only inherit one class at the same time, but can implement multiple interfaces at the same time)
 * 1,Implement Runnable interface (different from method 1)
 * 2,Implement the run() method
 * 3,Create a class object, and then create a Thread class object. Put the class that has just implemented the Runnable interface in the construction method to implement the Thread (different from method 1). Finally, call the start() method through the Thread class object to start the Thread
 */
public class ThreadTest implements Runnable{ //1. Implement Runnable interface

    //2. Implement run method
    @Override
    public void run() {
        for (int i = 0;i <= 10;i++){
            Thread.currentThread().setName("ThreadTest Thread of class");//Set the name of the current thread. It can not be set. There is a default value of Thread-0
            System.out.println(Thread.currentThread().getName()+""+i);//Get the current thread name
        }
    }
}

package com.company;

/**
 * The second way to implement threads is to inherit the Runnable interface (because the Thread class is implemented by implementing the Runnable interface)
 *      This method is applicable when threads need to be used and other classes need to be inherited (because a class can only inherit one class at the same time, but can implement multiple interfaces at the same time)
 * 1,Implement Runnable interface (different from method 1)
 * 2,Implement the run() method
 * 3,Create a class object, and then create a Thread class object. Put the class that has just implemented the Runnable interface in the construction method to implement the Thread (different from method 1). Finally, call the start() method through the Thread class object to start the Thread
 */
public class Main extends Thread{ //1. Inherit Thread class

    //2. Implement run method
    @Override
    public void run() {
        for (int i = 0;i <= 10;i++){
            Thread.currentThread().setName("Main Thread of class");//Set the name of the current thread. It can not be set. There is a default value of Thread-0
            System.out.println(Thread.currentThread().getName()+""+i);//Get the current thread name
        }
    }

    public static void main(String[] args) {

        //3. Creating a class object can be omitted, because the main method here is in this class, and it cannot be omitted in other cases
	    Main main = new Main();//Create a second

        //4. Call the method inherited from the Thread class to start the Thread
        main.start();//Start thread

        /**Method 2 different places*/
        //Create a class that has just implemented the Runnable interface
        ThreadTest threadTest = new ThreadTest();

        //Create a Thread class object and put the class that just implemented the Runnable interface into the construction method to implement the Thread
        Thread thread = new Thread(threadTest);

        //Call the start() method through the Thread class object
        thread.start();
    }
}

3. Classic ticket selling problem

First of all, using the method of inheriting Thread class to implement threads will cause the problem of shared resource synchronization. Of course, you can add the static keyword to solve it

Inheriting the Runnable interface implementation will not cause the problem of resource synchronization

It can be seen that the above results are still not what we want. Here is the real thread synchronization solution. Before learning, we need to learn the agent design pattern

4. Agent design pattern

Because of space, I put this part of the content in another blog. Please refer to https://blog.csdn.net/grd_java/article/details/109690730

5. Thread operation related methods

Methods inherited from the Object class
wait(): pauses the current thread and enters blocking. This is only a one-sided explanation. Because the wait method is shared by all classes, we usually pause the current shared resources (shared objects), which makes the thread unable to force the resources and forced into blocking
notify(): wakes up a suspended thread. Like wait, it wakes up a shared object
package com.company;

/**
 * Call dynamic proxy
 */
public class Main{

    public static void main(String[] args) {//First, the main function is a thread. We can test the method directly in the main method

        Thread thread = Thread.currentThread();//Gets the current thread object

        String threadName = thread.getName();//Gets the name of the current thread

        System.out.println(threadName);//The print result is main, because the main function is a thread

        System.out.println(thread.getId());//Get thread id

        /*
               The higher the value of thread priority, the higher the probability of obtaining priority, but it is not necessarily its priority, just the higher the probability
               Compared with low priority threads, high priority threads have a higher chance of grabbing cpu resources, but they can't grab low priority threads according to different situations
         */
        thread.setPriority(10);//Set the priority of thread to 10. Generally, the priority of most systems is 0-10. The middle value of 5 will be taken by default, while some systems are 0-100

        System.out.println(thread.getPriority());//Get thread priority

        System.out.println(thread.isAlive()?"Thread survival":"Thread death");//thread. The isalive () method is used to return the status of the thread. If it is still active, it returns True. Otherwise, it returns false

        try {
            thread.sleep(1000);//Sleep thread for 1 second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(thread.isAlive()?"Thread survival":"Thread death");

        thread.yield();//Pause the thread and let other threads execute first. At this time, the thread returns to the ready state and starts to grab the cpu
        System.out.println(thread.isAlive()?"Thread survival":"Thread death");

        try {
            thread.join();//Forcibly blocking threads. Generally, this method is used in other threads. When the thread calling this method finishes executing, it will be ready for the blocked thread again
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(thread.isAlive()?"Thread survival":"Thread death");

        thread.stop();//Forcibly stop the thread. It has been eliminated. It is not recommended. It is recommended to use yield
    }

}

2, Thread synchronization, thread deadlock

Deadlock: I asked Zhang San to deliver the goods first, and Zhang San asked me to give the money first. I refused to give the money first, and Zhang San refused to give the goods first. We each took a share of resources. Whoever refused to let go was called deadlock. Thread a takes a resource and thread b takes a resource. A wants b's current resources and b also wants a's current resources. If neither of them is willing to let go, it is called deadlock.

1. Thread safety, thread synchronization

When we sell tickets, there is a problem that several threads access resources at the same time. At this time, we need to use the thread synchronization scheme


Conditions for synchronization
There are at least two threads
Multiple threads must use the same resource
It must be ensured that only one thread can run in synchronization (in the code that needs synchronization, only one thread can work at the same time, and two threads cannot work at the same time)

1. Synchronous code block

synchronized (shared object, must be a subclass of object) {code}

package com.company;

import org.apache.commons.io.filefilter.TrueFileFilter;

public class ThreadTest implements Runnable{

    private int ticket = 10;
    @Override
    public void run() {
        boolean b = true;
        synchronized (this){//Synchronous code block
            while(b){
                if(ticket>0){
                    System.out.println(Thread.currentThread().getName()+"Is selling--"+(ticket--)+"--Ticket");
                }else{
                    b = false;
                }
            }
        }
    }
}

2. Synchronization method (the synchronization method can effectively solve the synchronization security problem, and does not need to know the shared object, and usually takes the current object as the synchronization object)

I forgot to delete the synchronization code block in the screenshot below, but the operation result is still normal. Please see the code I posted for details

package com.company;

import org.apache.commons.io.filefilter.TrueFileFilter;

public class ThreadTest implements Runnable{

    private int ticket = 10;
    @Override
    public void run() {
        boolean b = true;
        while(b){
            sale(b);//Call synchronization method
        }
    }

    public synchronized void sale(boolean b){//Put the code that needs to be synchronized in a method decorated with synchronized
        if(ticket>0){
            System.out.println(Thread.currentThread().getName()+"Is selling--"+(ticket--)+"--Ticket");
        }else{
            b = false;
        }
    }
}

3. Resolve the condition of thread exclusive resources

Carefully observe the synchronization code results mentioned above, and you will find that all tickets are sold by the same thread. How to solve it?

2. Thread producer and consumer issues

Producers continue to produce and consumers continue to take products produced by producers (producers continue to produce things to a certain area, and consumers continue to take products from this area)

Here we need to mention the methods inherited from the Object class again. Take them for your attention
wait(): pauses the current thread and enters blocking. This is only a one-sided explanation. Because the wait method is shared by all classes, we usually pause the current shared resources (shared objects), which makes the thread unable to force the resources and forced into blocking
notify(): wakes up a suspended thread. Like wait, it wakes up a shared object

package com.company;

/**
 * Commodity entity class
 */
public class Goods {
    private String brand;//brand
    private String name;//Trade name

    private boolean flag = false;//Judge whether there are goods in the warehouse. Yes is true and no is false

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * Thread safety synchronization, producer production method
     * @param brand
     * @param name
     */
    public synchronized void set(String brand,String name){
        //If the flag is true, it indicates that there are goods. Let the producer thread wait for the goods to be taken away and notify the consumer to take them
        if(flag){
            System.out.println("The producer checked the warehouse and found that the goods had not been taken away,==========Inform consumers to pick up");
            try {
                wait();//Waiting for the current shared resource goods is equivalent to blocking the current thread
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            Thread.sleep(1000);//Sleep for 1s
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        this.setBrand(brand);//Set brand
        this.setName(name);//Set name

        System.out.println("The producer produced"+this.getBrand()+"----"+this.getName());

        /**
         * After the producer has finished producing goods, set the flag to true to indicate that there are goods in the warehouse, and use notify to wake up the producer thread again
         */
        flag = true;

        notify();//Wake up the current shared resources so that the current thread can fight for the current shared resources, which is the effect of waking up the thread
    }

    public synchronized void get(){
        //If the flag is false, there is no product. Let the consumer produce quickly and pause the current thread
        if(!flag){
            System.out.println("Consumers come to pick things up and find no goods==========Inform the producer of production");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.getBrand();
        this.getName();
        System.out.println("The consumer took it"+this.getBrand()+"----"+this.getName());

        //After taking the goods, set the flag to false, indicating that the warehouse has no resources, and then wake up the thread
        flag = false;
        notify();
    }
}


3, Thread pool

Why do I need a thread pool

  • In practical use, threads occupy system resources very much. Improper management will lead to a series of system problems. Thread pools are used to manage threads in many concurrent frameworks
  • Thread pool is to create a pile of threads in the pool in advance, take them away when someone needs them, return them when they are used up, and give them to others to continue to use
  • Using thread pool can reuse existing threads to continue executing tasks, avoid repeated creation and destruction of threads, waste system resources and improve system response speed
  • Thread pool can reasonably manage threads and adjust the number and size of runnable threads according to the affordability of the system

The following conceptual pictures are from the courseware ppt of horse soldier education


1. CachedThreadPool, cache thread pool


package com.company;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main{

    public static void main(String[] args) {

        /**
         * Executors: Is the parent class of all thread pools. It provides the execute method to add threads to the thread pool
         */
        ExecutorService executorService = Executors.newCachedThreadPool();//Create cache thread pool

        for (int i = 0;i < 10;i++){
            executorService.execute(new ThreadTest());//Add thread
        }

        executorService.shutdown();//Close thread pool

    }
}

2. FixedThreadPool, a fixed number of thread pools

package com.company;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main{

    public static void main(String[] args) {

        /**
         * Executors: Is the parent class of all thread pools. It provides the execute method to add threads to the thread pool
         */
        ExecutorService executorService = Executors.newFixedThreadPool(5);//Create a fixed thread pool, that is, how many threads are fixed by everyone

        for (int i = 0;i < 10;i++){
            executorService.execute(new ThreadTest());//Add thread
        }

        executorService.shutdown();//Close thread pool

    }
}

3. Singlethreadexecution, SingleThread

package com.company;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main{

    public static void main(String[] args) {

        /**
         * Executors: Is the parent class of all thread pools. It provides the execute method to add threads to the thread pool
         */
        ExecutorService executorService = Executors.newSingleThreadExecutor();//Creating a singleton thread pool means that there is only one thread

        for (int i = 0;i < 10;i++){
            executorService.execute(new ThreadTest());//Add thread
        }

        executorService.shutdown();//Close thread pool

    }
}

4. ScheduledThreadPool, which can schedule the scheduled thread pool

package com.company;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Main{

    public static void main(String[] args) {

        /**
         * Executors: Is the parent class of all thread pools. It provides the execute method to add threads to the thread pool
         */
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);//Create a schedulable thread pool with a maximum capacity of 5

        /**
         * schedule(Runnable Interface, time (e.g. 5), unit (second, minute or hour))
         */
        scheduledExecutorService.schedule(new Runnable() {//Add an anonymous function thread and delay the execution by 3s
            @Override
            public void run() {
                System.out.println("Delay 3 seconds");
            }
        },3, TimeUnit.SECONDS);

        scheduledExecutorService.schedule(new ThreadTest(),3,TimeUnit.SECONDS);//Add ThreadTest thread class, delay 3 seconds
        
        /**
         * scheduleAtFixedRate(Runnable Interface, delay time, time between each execution, unit)
         */
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("Execution is delayed for 4 seconds, and then every 1 s Execute once"+"========="+(Thread.currentThread().getName()));
            }
        },4,1,TimeUnit.SECONDS);

//        scheduledExecutorService.shutdown();// Note that you cannot close the thread pool if you want to execute every second

    }
}

In addition, there is no change in the singleton mode except that multiple thread objects become one, so I won't introduce it more

Tags: Java Multithreading

Posted by spikypunker on Sat, 07 May 2022 01:55:53 +0300