Java multithreading (one)
As a very important knowledge point in Java, it is still necessary to summarize it here.
1. The life cycle of a thread and the five basic states
Regarding the life cycle of threads in Java, let’s first look at the following classic picture:
The above figure basically covers the important knowledge points of multi-threading in Java. Once you master the knowledge points in the above figure, you will basically master the multi-threading in Java. Mainly including:
Java threads have five basic states
New state (New): When a thread object pair is created, it enters a new state, such as: Thread t = new MyThread();
Ready state (Runnable): When the start() method of the thread object (t.start();), the thread enters the ready state. A thread in the ready state just means that the thread is ready and is waiting for the CPU to schedule execution at any time, not that the thread will execute immediately after t.start() is executed;
Running state: When the CPU starts to schedule threads in the ready state, the thread can be truly executed, that is, it enters the running state. Note: The ready state is the only entry to the running state, that is, if a thread wants to enter the running state to execute, it must first be in the ready state;
Blocked state: For some reason, a thread in the running state temporarily gives up its use of the CPU and stops execution. At this time, it enters the blocking state. It will not have the chance to be called by the CPU again to enter the running state. According to the reasons for blocking, blocking states can be divided into three types:
1. Waiting for blocking: The thread in the running state executes the wait() method to make the thread enter the waiting for blocking state;
2. Synchronized blocking-- The thread fails to acquire the synchronized synchronization lock (because the lock is occupied by other threads), it will enter the synchronized blocking state;
3. Other blocking--The thread will enter a blocking state by calling sleep() or join() of the thread or issuing an I/O request. When the sleep() state timed out, join() waited for thread to terminate or timed out, or I/O processing was completed, the thread re-entered to the ready state.
Dead: The thread has finished executing or exits the run() method due to an exception, and the thread ends its life cycle.
2. Creation and startup of Java multithreads
There are three basic forms of thread creation in Java
1. Inherit the Thread class and override the run() method of the class.
class MyThread extends Thread { private int i = 0; @Override public void run() { for (i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }} public class ThreadTest { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Thread myThread1 = new MyThread(); // Create a new thread myThread1 This thread enters the new state Thread myThread2 = new MyThread(); // Create a new thread myThread2 This thread enters the new state myThread1.start(); // Call the start() method to make the thread enter the ready state myThread2.start(); // Call the start() method to make the thread enter the ready state} } }}As shown above, inheriting the Thread class, a new thread class MyThread is defined by overwriting the run() method, where the method body of the run() method represents the task that the thread needs to complete, and is called the thread execution body. When creating this thread class object, a new thread is created and enters the new thread state. By calling the start() method referenced by the thread object, the thread enters the ready state. At this time, the thread may not be executed immediately, depending on the CPU scheduling timing.
2. Implement the Runnable interface and override the run() method of the interface. The run() method is also a thread execution body, create an instance of the Runnable implementation class, and use this instance as the target of the Thread class to create a Thread object. The Thread object is the real thread object.
class MyRunnable implements Runnable { private int i = 0; @Override public void run() { for (i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }} public class ThreadTest { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Runnable myRunnable = new MyRunnable(); // Create an object of Runnable implementation class Thread thread1 = new Thread(myRunnable); // Create a new thread with myRunnable as a Thread target Thread thread2 = new Thread(myRunnable); thread1.start(); // Call the start() method to make the thread enter the ready state thread2.start(); } } }} I believe that everyone is familiar with the above two ways to create new threads. So what is the relationship between Thread and Runnable? Let’s first look at the following example.
public class ThreadTest { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Runnable myRunnable = new MyRunnable(); Thread thread = new MyThread(myRunnable); thread.start(); } } }}class MyRunnable implements Runnable { private int i = 0; @Override public void run() { System.out.println("in MyRunnable run"); for (i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }}class MyThread extends Thread { private int i = 0; public MyThread(Runnable runnable){ super(runnable); } @Override public void run() { System.out.println("in MyThread run"); for (i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }}Similarly, the same is true for creating threads that implement the Runnable interface, the difference is that
1 Thread thread = new MyThread(myRunnable);
So can this method successfully create a new thread? The answer is yes. As for the thread execution body at this time, is the run() method in the MyRunnable interface or the run() method in the MyThread class? Through output, we know that the thread execution body is the run() method in the MyThread class. In fact, the reason is very simple, because the Thread class itself also implements the Runnable interface, and the run() method is first defined in the Runnable interface.
public interface Runnable { public abstract void run(); } Let's take a look at the implementation of the run() method in the Runnable interface in the Thread class:
@Override public void run() { if (target != null) { target.run(); } }That is to say, when executing the run() method in the Thread class, it will first determine whether the target exists. If it exists, the run() method in the target is executed, that is, the run() method in the class that implements the Runnable interface and overwrites the run() method. However, in the columns given above, due to the existence of polymorphism, the run() method in the Thread class is not executed at all, but the runtime type, that is, the run() method in the MyThread class is directly executed.
3. Create threads using Callable and Future interfaces. Specifically, it creates an implementation class for the Callable interface and implements the clall() method. And use the FutureTask class to wrap the Callable implementation class object, and use this FutureTask object as the target of the Thread object to create a thread.
It seems a bit complicated, but it will be clear if you look at an example directly.
public class ThreadTest { public static void main(String[] args) { Callable<Integer> myCallable = new MyCallable(); // Create MyCallable object FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); // Use FutureTask to wrap the MyCallable object for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Thread thread = new Thread(ft); //FutureTask object creates a new thread as a target of the Thread object thread.start(); //The thread enters the ready state} } System.out.println("The main thread for loop has been executed.."); try { int sum = ft.get(); //Get the result returned by the call() method in the newly created new thread System.out.println("sum = " + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }}class MyCallable implements Callable<Integer> { private int i = 0; // Unlike the run() method, the call() method has a return value @Override public Integer call() { int sum = 0; for (; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); sum += i; } return sum; }}First of all, we found that in implementing the Callable interface, the run() method is no longer the run() method, but the call() method. This call() method is the thread execution body and also has a return value! When creating a new thread, the MyCallable object is wrapped through FutureTask and also serves as a target for the Thread object. Then look at the definition of the FutureTask class:
public class FutureTask<V> implements RunnableFuture<V> { //.... } public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }Therefore, we found that the FutureTask class actually implements both Runnable and Future interfaces, which makes it have the dual characteristics of Future and Runnable. Through the Runnable feature, it can be used as a target of the Thread object, and the Future feature allows it to obtain the return value of the call() method in the newly created thread.
After executing this program, we find that sum = 4950 is always the last output. "The main thread for loop has been executed..." is likely to be output in the middle of the child thread loop. From the CPU's thread scheduling mechanism, we know that there is no problem with the output timing of "the main thread for loop has been executed...", so why will sum =4950 be output forever?
The reason is that when the child thread's call() method is obtained through the ft.get() method, when the child thread's method has not been executed yet, the ft.get() method will block until the call() method is executed before the return value can be obtained.
The above mainly explains three common thread creation methods. For the startup of threads, they are all called the start() method of the thread object. It is important to note that the start() method cannot be called twice on the same thread object.
III. Ready, Run and Death Status of Java Multithreading
The ready state is converted to the running state: when this thread gets the processor resource;
The running state is converted to the ready state: when this thread actively calls the yield() method or loses processor resources during running.
The running state is converted to the dead state: When the thread execution body is completed or an exception occurs.
It should be noted here that when the yield() method of the thread is called, the thread transitions from the running state to the ready state, but which thread in the ready state of the CPU is scheduled has a certain randomness. Therefore, it may occur that after the A thread calls the yield() method, the CPU still schedules the A thread.
Due to actual business needs, it is often encountered that a thread needs to be terminated at a specific opportunity to make it enter a dead state. The most common method at present is to set a boolean variable, and when the conditions are met, the thread execution body will be executed quickly. like:
public class ThreadTest { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { thread.start(); } if(i == 40){ myRunnable.stopThread(); } } } } class MyRunnable implements Runnable { private boolean stop; @Override public void run() { for (int i = 0; i < 100 && !stop; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } public void stopThread() { this.stop = true; }}We will continue to sort out related articles in the future. Thank you for your support for this site!
Series of articles:
Explanation of Java multi-threaded instances (I)
Detailed explanation of Java multi-threaded instances (II)
Detailed explanation of Java multi-threaded instances (III)