Basic principles of Java multithreading

Keywords: Java Back-end

Process and thread

Before learning Java multithreading, we need to understand the difference between process and thread.

Process is a dynamic execution process of a program. It needs to go through a complete process from code loading, code execution to execution. This process is also the process of the process itself from generation, development to final demise. Multi process operating system can run multiple processes (programs) at the same time. Because the CPU has time-sharing mechanism, each process can cycle to obtain its own CPU time slice. Because the CPU executes very fast, all programs seem to run at the same time.

Multithreading is an effective means to realize concurrency mechanism. Like threads, processes are a basic unit of concurrency. Threads are smaller execution units than processes. Threads are further divided on the basis of processes. Multithreading means that a process can produce multiple smaller program units during execution. These smaller units are called threads. These threads can exist and run at the same time. A process may contain multiple threads executing at the same time.

Implementation mode

There are two ways to implement multithreading in Java. One is to inherit the Thread class, and the other is to implement the Runnable interface. Let's introduce the use of these two methods respectively. Because it is relatively simple, directly paste the implementation code.

Implement Runnable interface

public class RunnableDemo implements Runnable {

    public static void main(String[] args) {
        //You can set the thread name directly in the constructor
        new Thread(new RunnableDemo(), "Thread 1").start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            // Gets the current thread name
            System.out.println(Thread.currentThread().getName() + "=======" + i);
        }
    }

}

Inherit Thread class

public class ThreadDemo extends Thread {

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.setName("thread1"); //Set thread name
        threadDemo.start(); // Start thread
    }

    // Override thread run method
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            // Gets the current thread name
            System.out.println(Thread.currentThread().getName() + "=======" + i);
        }
    }

}

Start mode

The thread is started through the start() method instead of the run() method! The run method is the execution method in the thread!

  • start() method to start the Thread, which really realizes multi-threaded operation. At this time, you can continue to execute the following code directly without waiting for the Run method body code to be executed; Start a Thread by calling the start() method of the Thread class. At this time, the Thread is ready and not running. Then, this Thread class calls the method run() to complete its operation. Here, the method run() is called the Thread body, which contains the content of the Thread to be executed. The Run method ends and the Thread terminates. The CPU then schedules other threads.
  • The run () method is called as a normal method..

Basic principles of multithreading

Next, let's understand the basic principle of multithreading. The overall principle is as follows.

After we call the start method of the thread, we actually do a lot of things at the bottom. The specific implementation diagram is as follows. It is not necessarily neat, but it can express the general meaning.

There are many OS scheduling algorithms, such as first come first service scheduling algorithm (FIFO), shortest priority (i.e. priority scheduling for short jobs), time slice rotation scheduling, etc. This part belongs to the relevant knowledge of the operating system.

General process:

  • Writing ThreadDemo code
  • Java starts the thread through the start() method
  • Start method of starting system thread in JVM
  • Call different execution methods according to the operating system
  • Thread execution

The running state of the thread

Generally speaking, in Java, there are six thread states, namely:

  • NEW: in the initial state, the thread is built, but the start method has not been called

  • RUNNABLED: running state. JAVA threads call the ready and running states of the operating system "running"

  • BLOCKED: the BLOCKED state indicates that the thread enters the waiting state, that is, the thread gives up the CPU usage right for some reason. Blocking can also be divided into several cases

    • Wait blocking: the running thread executes the wait method, and the jvm will put the current thread into the wait queue
    • Synchronization blocking: when a running thread obtains the synchronization lock of an object, if the synchronization lock is occupied by other thread locks, the jvm will put the current thread into the lock pool
    • Other blocking: when the running thread executes the Thread.sleep or t.join method, or issues an I/O request, the JVM will set the current thread to the blocking state. When the sleep ends, the join thread terminates, and io processing is completed, the thread resumes
  • WAITING: WAITING state

  • TIME_WAITING: timeout waiting status. It will be returned automatically after timeout

  • TERMINATED: TERMINATED status, indicating that the current thread has completed execution

Thread termination

How to stop a thread correctly? There are still many things to say about this issue.

We know that Thread provides some operation methods of threads, such as stop, suspend, etc. these methods can terminate a Thread or suspend a Thread, but these methods are not recommended. The reason is simple:

For example, suppose that there are multiple tasks executing in a thread. At this time, if the stop method is called to forcibly interrupt, it is equivalent to sending an instruction to tell the operating system to end the thread, but the completion of the end action of the operating system does not mean that the task in the thread is completed, It is likely that half of the task execution of the thread is forcibly interrupted, resulting in data problems. This behavior is similar to executing kill -9 in linux system, which is an unsafe operation.

So, in addition to this method, what other methods can realize thread termination? To understand this problem, we first need to know when a thread is terminated.

When does a thread end execution

Let's analyze the following code. After starting a thread through start (), it is essentially the run method of the thread.

If the thread is running until the run method is executed, and the instructions in the run method are executed, the thread will be destroyed.

public class MyThread extends Thread { 
    public void run() { 
        System.out.println("MyThread.run()"); 
    } 
}
MyThread myThread1 = new MyThread(); 
myThread1.start();

Under normal circumstances, this thread does not need human intervention to end. If you want to force the end, you can only use the stop method.

In which cases do thread interrupts require external intervention?

  • There is an infinite loop execution in the thread, such as a while(true) loop

  • There are some blocking operations in the thread, such as sleep, wait, join, etc.

Thread with loop

Suppose the following scenario exists. In the run method, there is a while loop, because the existence of this loop makes the run method unable to run and end. In this case, how to terminate?

public class MyThread extends Thread { 
    public void run() { 
        while(true){ System.out.println("MyThread.run()"); } 
    } 
}
MyThread myThread1 = new MyThread(); 
myThread1.start();

According to our development thinking, the first thing to be solved is that the while(true) loop must have an end condition, and the second is to modify the end condition elsewhere to make the thread aware of the change. Suppose we change while(true) to while(flag). This flag can be externally modified as a shared variable. After modification, the loop conditions cannot be met, so as to exit the loop and end the thread.

This logic is actually very simple. In fact, it gives the thread an exit condition. Without this condition, the thread will run all the time.

In fact, an interrupt method is provided in Java, which implements thread interrupt operation. Its function is the same as that of the above case.

When other threads call the interrupt method of the current thread, it means to say hello to the current thread and tell him that the execution of the thread can be interrupted. When to interrupt depends on the current thread itself.

The thread makes corresponding by checking whether the resource is interrupted. You can judge whether it is interrupted by isInterrupted().

public class InterruptDemo {

    private static int i;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                //By default, isInterrupted returns false and becomes true through thread.interrupt
                i++;
            }
            System.out.println("Num:" + i);
        }, "interruptDemo");
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        //thread.interrupt(); // Plus and no effects
    }
}

This way of identifying bits or interrupting operations can give the thread the opportunity to clean up resources when terminating, rather than arbitrarily stopping the thread. Therefore, this method of terminating the thread is more safe and elegant.

Thread interrupt in blocked state

In another case, when the thread is blocked, I want to interrupt the thread. What should I do?

public class InterruptDemo {

    private static int i;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                //By default, isInterrupted returns false and becomes true through thread.interrupt
                // Cycle state
                //i++;

                //Blocking state
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Num:" + i);
        }, "interruptDemo");
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        //thread.interrupt(); // Plus and no effects
    }
}

The feedback from this example shows that sleep, wait, join and other operations we usually use in threads will throw an InterruptedException exception. Why the exception is thrown is because it must be able to respond to a response after an interrupt request is initiated by other threads during blocking, This response is reflected through InterruptedException.

However, it should be noted here that if we do not handle this exception, we cannot interrupt the thread, because the current exception only responds to the external interrupt command for this thread, and the interrupt state of the thread will be reset. If you need to interrupt, you also need to add the following code in catch:

//Blocking state
try {
    TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
    e.printStackTrace();
    Thread.currentThread().interrupt(); //Interrupt again
}

Therefore, throwing an InterruptedException exception does not mean that the thread must terminate, but reminds the current thread of an interrupted operation. The next processing depends on the thread itself, such as:

  • Directly catch exceptions without any processing
  • Throw the exception out
  • Stop the current thread and print exception information

Posted by clem_c_rock on Fri, 05 Nov 2021 16:15:02 -0700