JAVA - API - process multithreading

Process and thread

process

Concept of process

A process is a running program that occupies the corresponding memory area and is executed and calculated by the CPU.

Characteristics of the process

  • Independence
    Process is an independent entity in the system. It can have its own independent resources. Each process has its own private address space. Without the permission of the process itself, a user process cannot directly access the address space of other processes
  • Dynamics
    The difference between process and program is that program is only a static instruction set, while process is an instruction set active in the system. After adding the concept of time, the program is called process, which has its own life cycle and various states. These concepts are not possessed by the program
  • Concurrency
    Multiple processes can execute concurrently on a single processor CPU, and multiple processes will not affect each other

thread

Thread concept

Thread is the smallest unit that OS can schedule operations. It is included in the process and is the actual operation unit in the process
A process can start multiple threads, one of which calls other threads in the process.
The process switching we see is also the main thread of different processes
Multithreading allows the same process to process multiple tasks simultaneously, which is equivalent to expanding the function of the process.

Relationship between process and thread

An operating system can have multiple processes. A process can contain one thread (single threaded program) or multiple threads (multi-threaded program)

Each thread not only shares the memory of the same process, but also has its own independent memory space
Therefore, if you want to use threading technology, you must first have a process. The process is created by the OS operating system, usually in C or C + +

Characteristics of multithreading

Randomness

We think that multiple processes run at the same time on the macro level, but on the micro level, a CPU [single core] can only execute one thread in a process.
So why does it look like multiple processes are executing at the same time?
This is because the CPU switches efficiently at the nanosecond level or even faster, which exceeds people's reaction speed, which makes each process seem to be running at the same time. In other words, on the macro level, all processes seem to be parallel [running at the same time], but on the micro level, they are serial [one CPU can only process one thing at a time].

Serial and parallel
Serial means that a CPU can only process one thing at a time, similar to a single lane
Parallel means that multiple CPU s can handle multiple things at the same time, similar to multi lane

CPU time-sharing scheduling

Time slice, that is, a time period allocated by the CPU to each thread, is called its time slice, that is, the time that the thread is allowed to run. If the thread is still executing when the time slice is used up, the CPU will be deprived and allocated to another thread to suspend the current thread. If the thread blocks or ends before the time slice is used up, the CPU will switch immediately to avoid waste of CPU resources, When you switch to the previously suspended thread again, restore the scene and continue execution.
Note: we cannot control which threads the OS chooses to execute. The underlying OS has its own rules, such as:

  1. FCFS(First Come First Service algorithm)
  2. SJS(Short Job Service short service algorithm)

Status of the thread

Due to the complexity of thread states, we first learn the three basic thread states and their transitions, referred to as the "three state model":

  • Ready (runnable) status: the thread is ready to run and can be executed immediately as long as it obtains the CPU
  • Execution (running) state: the state in which the thread has obtained the CPU and its program is running
  • Blocking state: the state in which the running thread is temporarily unable to execute due to some events (I/O requests, etc.), that is, thread execution blocking

Ready → execute: allocate CPU for the ready thread to change to the execution state“
Execute → ready: the executing thread is deprived of the CPU to suspend execution due to the running out of time slices, and becomes ready
Execute → block: if the executing thread is blocked and cannot be executed due to an event, it will change from execution to blocking
(for example, a thread is accessing a critical resource while the resource is being accessed by another thread)
On the contrary, if the previously required resources are obtained, it changes from blocking to ready state and waits for the allocated CPU to execute again

We can add two more states:

  • Creation status: the creation of a thread is complex. You need to apply for a PCB first, then allocate necessary resources for the thread to run, and turn the thread into ready status and insert it into the ready queue
  • Termination status: wait for the OS to deal with the aftermath, and finally clear the PCB and return the PCB to the system

PCB(Process Control Block): in order to ensure that each thread participating in concurrent execution can run independently, the OS is configured with a unique data structure PCB to describe the basic situation and activity process of threads, so as to control and manage threads

Thread status and code comparison


Thread life cycle has five main states:

  1. New state: after the thread object is created, it enters the new state. For example, Thread t = new MyThread();
  2. Runnable: when the start() method of the thread object is called, the thread enters the ready state
    A thread in the ready (runnable) state only indicates that the thread is ready to wait for the CPU to schedule execution at any time. It does not mean that the thread will execute immediately after t.start() is executed
  3. Running: when the CPU schedules a thread in the ready state, this thread is the real execution, that is, it enters the running state
    The ready state is the only entry into the running state, that is, if a thread wants to enter the running state for execution, it must be in the ready state first
  4. Blocked: a thread in the running state temporarily gives up the right to use the CPU and stops execution for some reason. At this time, it enters the blocked state and has no chance to be selected by the CPU for execution again until it enters the ready state
    According to different causes of blocking States, blocking states can be subdivided into three types:
    Wait blocking: the thread in the running state executes the wait() method, and the thread enters the wait blocking state
    Synchronization blocking: if a thread fails to acquire a synchronized synchronization lock (because the lock is occupied by other threads), it will enter the synchronization blocking state
    Other blocking: when calling the thread's sleep() or join() or issuing an I/O request, the thread will enter the blocking state. When the sleep() state times out. Join() waits for the thread to terminate or time out or the I/O processing is completed, the thread will return to the ready state
  5. Dead: when the thread has finished executing or exited the run() method due to an exception, the thread ends its life cycle

Multithreaded code creation method 1: inherit Thread

summary

The Thread class is essentially an instance that implements the Runnable interface and represents an instance of a Thread
The only way to start a Thread is through the start() instance method of the Thread class
The start() method is a native method, which will notify the underlying operating system. Finally, the operating system starts a new thread, and the operating system will execute run()
Multithreading implemented in this way is very simple. You can automatically start a new thread and execute your own defined run() method by directly extending thread through your own class and overriding the run() method
Simulate opening multiple threads, and each thread calls the run() method

common method

Construction method

Thread() assigns a new thread object
Thread(String name) assigns a new thread object. Name - the name of the new thread
Thread(Runnable target) assigns a new thread object
Thread(Runnable target,String name) assigns a new thread object name - the name of the new thread

Common method

static Thread currentThread( )
Returns a reference to the thread object currently executing
long getId()
Returns the identity of the thread
String getName()
Returns the name of the thread
void run()
If the thread is constructed with a separate Runnable running object, the run method of the Runnable object is called
static void sleep(long millions)
Hibernates (pauses) the currently executing thread for a specified number of milliseconds
void start()
Start the thread: the Java virtual machine calls the thread's run()

Test how multithreading is created 1

Create package: cn.tedu.thread
Create class: TestThread1.java

package cn.tedu.thread;
/*This class is used for multithreading programming implementation scheme 1: inherit the Thread class to complete*/
public class TestThread1 {
    public static void main(String[] args) {
        //4. Create a thread object for testing
        /*4.new This corresponds to the new state of the thread
        * 5.To simulate multithreading, you must start at least two threads. If you only start one, it is a single threaded program*/
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();
        MyThread t4 = new MyThread();
        /*6.If this run() is called directly in this way, there is no effect of multithreading preempting execution
        * Just regard these two sentences as the call of ordinary methods. Whoever writes first will execute first*/
        //t1.run();
        //t2.run();
        /*7.start()The corresponding state is the ready state, which will add the newly created thread to the ready queue
        * As for when to execute, it is the effect of multi-threaded execution. You need to wait for the OS to select and allocate CPU
        * 8.When executing, the bottom layer of start() will automatically call the run() business we rewrite
        * 9.The execution of threads is random, that is, how t1-t4 is executed
        * Depending on the CPU scheduling and time slice allocation, we can't decide*/
        t1.start();//Start thread 1 in a multithreaded manner to turn the current thread into a ready state
        t2.start();//Start thread 2 in a multithreaded manner to turn the current thread into a ready state
        t3.start();//Start thread 3 in a multithreaded manner to turn the current thread into a ready state
        t4.start();//Start thread 4 in a multithreaded manner to turn the current thread into a ready state
    }
}

//1. Customize a multithreading class, and then let this class inherit Thread
class MyThread extends Thread{
    /*1.Scheme 1 of multithreading programming: it is completed by inheriting Thread class and overriding run() */
    //2. Rewrite run (). Run () contains our own business
    @Override
    public void run() {
        /*2.super.run()It refers to the business of calling the parent class. Now we want to use our own business, so comment it out*/
        //super.run();
        //3. Complete the business: print the name of the thread currently executing 10 times
        for (int i = 0; i < 10; i++) {
            /*3.getName()Indicates that you can get the name of the thread currently executing
            * Since this class inherits the Thread class, you can use this method directly*/
            System.out.println(i+"="+getName());
        }
    }
}

Multithreaded code creation method 2: implement Runnable interface

summary

If your own class already extends another class, you cannot inherit more. At this time, you can implement a Runnable interface

common method

When void run() creates a thread using the object that implements the interface Runnable, it will cause the run() method to call the object in the thread that is executed independently.

Test how multithreading is created 2

Create package: cn.tedu.thread
Create class: Thread2.java

package cn.tedu.thread;
/*This class is used for multithreading programming implementation scheme 2: it is completed by implementing the Runnable interface*/
public class TestThread2 {
    public static void main(String[] args) {
        //5. Create custom class object -- target business class object
        MyRunnable target = new MyRunnable();
        //6. How to start a Thread? You don't have one. You need to establish a relationship with Thread
        Thread t1 = new Thread(target);
        Thread t2 = new Thread(target);
        Thread t3 = new Thread(target);
        Thread t4 = new Thread(target);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

//1. Custom multithreading class
class MyRunnable implements Runnable{
    //2. Add the abstract method run() in the parent interface, which contains its own business
    @Override
    public void run() {
        //3. Write the service and print the name of the currently executing thread 10 times
        for (int i = 0; i < 10; i++) {
            /*Problem: neither the custom class nor the parent interface Runnable has a method to get the name
            * Therefore, you also need to find from the Thread:
            * currentThread():Static method to get the thread object currently executing
            * getName():Gets the name of the current thread*/
            System.out.println(i+"="+Thread.currentThread().getName());
        }
    }
}

Comparison of two implementation methods

  • Inherit Thread class
    Advantages: it is easy to write. If you need to access the current thread, you do not need to use the Thread.currentThread() method. You can directly use this to obtain the current thread
    Disadvantages: the custom Thread class has inherited the Thread class, so other classes cannot be inherited later
  • Implement Runnable interface
    Advantages: the custom thread class only implements the Runnable interface or Callable interface, and can inherit other classes later. In this way, multiple threads can share the same target object, so it is very suitable for multiple threads to process the same resource, so that the CPU, code and data can be separated (decoupled) to form a clear model, It better embodies the idea of object-oriented
    Disadvantages: the programming is slightly complex. If you want to access the current thread, you need to use the Thread.currentThread() method

Ticket selling cases

Demand: four ticket windows are designed, with a total of 100 tickets. Use multithreaded programming and write code

Scenario 1: inherit Thread

Create package: cn.tedu.tickets
Create class: TestThread.java

package cn.tedu.tickets;
/*Requirements: design a multi-threaded programming model, and sell 100 tickets in total in 4 windows
* This scheme uses multithreading programming scheme 1 to inherit the Thread class*/
public class TestThread {
    public static void main(String[] args) {
        //5. Create multiple thread objects
        TicketThread t1 = new TicketThread();
        TicketThread t2 = new TicketThread();
        TicketThread t3 = new TicketThread();
        TicketThread t4 = new TicketThread();
        //6. Start in a multithreaded manner
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

//1. Customize multi-threaded ticketing class and inherit Thread
class TicketThread extends Thread{
    //3. Define variables to save the number of votes to be sold
    /*Problem: a total of 400 tickets were sold for the four thread objects. The reason is that the objects were created four times and their member variables were operated respectively
    * Solution: let all objects share the same data, and the number of votes needs to be set to static*/
    static int tickets = 100;
    //2. Rewrite the run() of the parent class, which is our business
    @Override
    public void run() {
        //4.1 circulation ticket selling
        while(true){
            try {
                //7. Let each thread go through sleep and increase the frequency of thread state switching and the probability of error
                //Question 1: resale occurs: multiple people sell the same ticket
                //Question 2: oversold occurred: the specified number of votes exceeded 100, and there were 0 - 1 - 2 tickets
                Thread.sleep(10);//Let the current thread sleep for 10ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //4.2 print the name of the thread currently selling tickets, and the number of votes is - 1
            System.out.println(getName()+"="+tickets--);
            //4.3 make judgment. If there is no ticket, exit the dead cycle
            if(tickets <= 0) break;//Note that the outlet must be set for the dead cycle
        }
    }
}

Scheme 2: implement Runnable

Create package: cn.tedu.tickets
Create class: TestRunnable.java

package cn.tedu.tickets;
/*Requirements: design a multi-threaded programming model, and sell 100 tickets in total in 4 windows
 * This scheme uses multithreading programming scheme 2 to implement the Runnable interface*/
public class TestRunnable {
    public static void main(String[] args) {
        //5. Create the implementation class object of Runnable interface as the target business object
        TicketRunnable target = new TicketRunnable();
        //6. Create multiple thread class Thread objects and hand over the target business object to multiple thread objects for processing
        Thread t1 = new Thread(target);
        Thread t2 = new Thread(target);
        Thread t3 = new Thread(target);
        Thread t4 = new Thread(target);
        //7. Start multiple threaded objects in a multithreaded manner
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

//1. Customize the multithreading class to implement the Runnable interface
class TicketRunnable implements Runnable{
    //3. Define a member variable to save 100 votes
    /*Since the custom class object is created only once, the votes are shared by all Thread objects and Thread class objects*/
    int tickets = 100;
    //2. Add the methods that are not implemented in the interface. The methods are our business
    @Override
    public void run() {
        //4.1 circulation ticket selling
        while(true){
            //8. Let the thread sleep for 10ms to increase the probability of thread state switching and error
            try {
                Thread.sleep(10);//Let the current thread sleep for 10ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //4.2 print the name of the thread currently selling tickets & number of tickets - 1
            System.out.println(Thread.currentThread().getName()+"="+tickets--);
            //4.3 set the exit of dead circulation and stop selling tickets when there are no tickets
            if(tickets <=0 ) break;
        }
    }
}

problem

  1. Every time you create a thread object, you will generate a tickets variable with the value of 100. After creating the object four times, 400 tickets will be generated. Do not meet the needs, how to solve it? Whether the tickets variable can be shared among each object ensures that how many objects sell these 100 tickets.
    Solution: use static decoration
  2. Oversold, 0, - 1, - 2.
  3. If resale occurs, the same ticket is sold to multiple people.
  4. How do multithreading security problems arise? The common situation is due to the randomness of threads + access latency.
  5. How to judge whether the program has thread safety problems in the future?
    In multithreaded programs + shared data + multiple statements operate on shared data
    Solution: synchronization lock

Tags: Java Back-end thread api

Posted on Mon, 22 Nov 2021 13:28:59 -0500 by ZachEdwards