Java Concurrent Programming: Thread Life Cycle

Keywords: Java Programming jvm

Preface:

In the previous article, we learned the differences between processes and threads, and the advantages and disadvantages of using multithreading. This article focuses on how to create a thread in Java and the lifecycle of a thread.

Interview Questions

Q: How are threads implemented?
What is the difference between Q:start() and run()?
Q: Thread life cycle and state?

Thread life cycle

1.1 How to create a thread

1.1.1 Implementing Runnable Interface

_Implement the Runnable interface and override the run method.
_Runnable means "task", which is performed by implementing the Runnable interface, defining a subtask to be executed by a Thread object. The implementation of Runnable must be a parameter of the Thread class, and then creating a new thread to execute the task by calling the Thread start method.If you call the run method directly, it will be called as a normal method of the current thread and no new thread will be created.

    public class MyThread implements Runnable {
        public void run() {
            System.out.println("MyThread");
        }
        
        public static void main(String[] args){
            new MyThread().start();
        }
    }

1.1.2 Inherits the Thread class

_Inherits the Thread class, overrides the run method, which defines what the thread will do.
_Thread is a thread class in java. A thread object can be created by new Thread(). JVM creates an operating system-level thread from this thread object. What this thread does is what is in the run method. The Thread class implements the Runnable interface. We create threads by inheriting the Thread class, which is equivalent to how the Runnable interface is implementedA simplification has been made that does not require the task to be executed by creating a Thread object separately, since it is a Thread object itself, and the overridden run method defines the content of the task to be executed. You can call your own start method directly to start a new thread to execute the task.The disadvantage is that Java is single inherited, and if you need to inherit other classes, you can only choose the Runnable interface.

    public class MyThread extends Thread {
        public void run() {
            System.out.println("MyThread");
        }
        
        public static void main(String[] args){
             new MyThread().start();
        }
    }

1.1.3 Implementing Callable Interface

_Implements the Callable interface, overrides the call method, and defines return value types in generics.
_Runnable's execution has no return value, Callable can return the execution result.Encapsulates a task with a return value through the FutureTask class, delegates the task to the Thread class for execution, and gets the execution result through the FutureTask.FutureTask implements the RunnableFuture interface, and RunnableFuture implements the Runnable and Future interfaces, respectively.The Runnable interface provides the run method so that it can be executed by a thread, and the Future interface provides the get method so that it can get the return value.

    public class MyThread implements Callable<String> {
        public String call() throws Exception {
            return "Callable";
        }
        
        public static void main(String[] args){
            FutureTask<String> task = new FutureTask<>(new MyThread()));
            new Thread(task).start();
            System.out.println(task.get());
        }
    }

Future mode

_We know the inheritance structure of the FutureTask interface and what it does, but how it does that.
_Following is a simple simulation of the implementation principle of Callable, only the implementation of the Future interface get method, using FutureData
To simulate the FutureTask class.

        public interface MyFuture{
            String get();
        }

        public class FutureData implements Runnable, MyFuture {
            Callable<String> callable;
            private String result;

            public FutureData(Callable callable) {
                this.callable = callable;
            }

            public String get() {
                return result;
            }

            public void run() {
                try {
                    result = callable.call();
                } catch (Exception e) {
                    result = null;
                }
            }
        }

        public class MyThread implements Callable<String> {
            public String call() {
                return Thread.currentThread().getName() + " call()";
            }

            public static void main(String[] args) throws ExecutionException, InterruptedException {
                FutureTask<String> task = new FutureTask<>(new MyThread());
                new Thread(task).start();
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(task.get());

                FutureData data = new FutureData(new MyThread());
                new Thread(data).start();
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(data.get());
            }
        }

        //Ouput
        // Thread-0 call()
        // Thread-1 call()

_From the example above, you can see that there are two fields inside FutureData, Callable to receive the task you defined, and result to store the return value of the task.After start (), a separate thread is opened up to perform the task, which does not affect the work of the main thread. In the previous article, asynchronous event handling, one of the advantages of multithreading, was mentioned, making the program more responsive.This pattern is reflected.After the task is executed, store the return value in the reslut and wait for it to be taken out by other threads. If other threads call the get method to get the value in the results before the task is executed, they will only get null. This has the problem whether the result of the task execution is null or the task has not started execution.In FutureTask, a flag is set to determine the current status of the task. If the task is not executed or not completed, the get () method will be blocked until the task is completed.

1.1.4 Some personal understanding of creating threads

_Individually, I think there is only new Thread() to create threads. There are two ways to define tasks: Runnable interface and Callable interface. Inheriting Thread class to implement Runnable interface indirectly and Runnable interface directly is to override Run method. This method defines what the threads need to do, more like a task, if the interface name is changed to Task interfaceBetter yet, the run method does not return a value, and we can define a task with a return value by implementing the call method of the Callable.

Life cycle and state of 1.2 threads

_The state of threads in Java differs from that in the operating system.The following are the states of threads in Java that are exposed to us at the virtual machine level, as explicitly defined in the enumeration class Thread.State.

  • Initial state: A thread is in this state when it is created and has not yet called the start() method.At this point it has allocated the necessary system resources and performed the initialization.Threads are not currently eligible for CPU time slices.
        Thread thread = new Thread("MyThread");
        System.out.println(thread.getName()+"Current status:"+thread.getState());
        
        //Output
        // MyThread Current Status: NEW
  • Running state: Initially calling the start() method will enter the current state, and the thread is already eligible for CPU time slices. In this state, as long as the dispatcher assigns time slices to the thread, the thread can run and be in the running state; if not, it can only wait for time slices to be acquired, and the thread is in the ready state, collectively referred to as the running state.
        Thread thread = new Thread("MyThread");
        thread.start();
        System.out.println(thread.getName()+"Current status:"+thread.getState());
                
        //Output
        // MyThread Current Status: RUNNABLE
  • Blocking state: A thread could have run but was prevented from running because it was waiting to acquire a lock.When a thread is blocked, the scheduler ignores the thread and does not allocate any CPU time to the thread.It is not possible for a thread to perform an operation until it has re-entered the ready state.
        Object lock = new Object();
        Thread thread = new Thread("MyThread") {
            public void run() {
                synchronized (lock) {
                    while (true) {}
                }
            }
        };

        Thread thread2 = new Thread("MyThread2") {
            public void run() {
                synchronized (lock) {
                    while (true) {}
                }
            }
        };
        thread.start();
        thread2.start();
        Thread.sleep(100);
        System.out.println(thread.getName() + "Current status:" + thread.getState());
        System.out.println(thread2.getName() + "Current status:" + thread2.getState());

        //Output
        // MyThread Current Status: RUNNABLE
        // MyThread2 Current Status: BLOCKED
  • Wait state: When a thread enters the wait state, it is not divided into time slices, cannot continue execution, and must be operated by other threads in order to be waked up.
        Thread thread = new Thread("MyThread") {
            public void run() {
                synchronized (this) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread.start();
        Thread.sleep(100);
        System.out.println(thread.getName() + "Current status:" + thread.getState());

        //Output
        // MyThread Current Status: WAITING
  • Timeout wait state: Similar to wait state, the difference is that it can wait for a while before a thread can wake itself up.
        Thread thread = new Thread("MyThread") {
            public void run() {
                synchronized (this) {
                    try {
                        wait(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread.start();
        Thread.sleep(100);
        System.out.println(thread.getName() + "Current status:" + thread.getState());

        //Output
        // MyThread Current Status: TIMED_WAITING
  • Terminate state: A thread in the terminate state will no longer be schedulable, will no longer get CPU time, and its task has ended.Task termination is usually done by the run() method or the program throws an exception.
        Thread thread = new Thread("MyThread") {
            public void run() {
                System.out.println((Thread.currentThread().getName() + " completion of enforcement"));
            }
        };
        Thread thread2 = new Thread("MyThread2") {
            public void run() {
                throw new RuntimeException("Program execution error");
            }
        };
        thread.start();
        thread2.start();
        thread.join();
        thread2.join();
        System.out.println(thread.getName() + "Current status:" + thread.getState());
        System.out.println(thread2.getName() + "Current status:" + thread2.getState());

        //Output
        // MyThread Execution Completed
        // Exception in thread "MyThread2" java.lang.RuntimeException: Program execution error
        //      at tmp.NewThreadTest$4.run(NewThreadTest.java:47)
        // MyThread Current Status: TERMINATED
        // MyThread2 Current Status: TERMINATED

Critical zone

Threshold zones are used to represent a common resource, or shared data, and can be used by multiple threads, but only one thread at a time can use them. Once a critical zone resource is occupied, other threads must wait to use it.

Blocking and Non-Blocking

https://blog.csdn.net/history...

_Blocking non-blocking is about threads and processes.
Blocking means that the calling thread or process is suspended by the operating system.Non-blocking means that the calling thread or process will not be suspended by the operating system.

_Blocked and non-blocked are commonly used to describe the interaction between multiple threads, such as when a thread occupies a critical zone resource, all other threads that need that resource must wait outside that critical zone, and waiting causes the thread to hang.This is blocking.At this point, if the resource-consuming thread has been reluctant to release the resource, all other threads blocking this critical zone will not work.

Reference

Java Concurrent Programming Practice
Java Programming Ideas (4th Edition)
  https://blog.csdn.net/justlov...
  https://snailclimb.gitee.io/j...

Thanks for reading!

If you want to make a change, you must abandon it.

Posted by AshleyByrom on Mon, 02 Dec 2019 07:34:34 -0800