Java Concurrency Learning Series (1)

Java Concurrency Series Learning (1)

As we all know, it Java并发系列编程has always been a mountain that Java programmers can't easily bypass, which can be described as a small mountain. The Java ecosystem provides a very rich concurrent programming class library, but this has also created a lot of people who know it but don't know why, many people only use it, but don't know its underlying operating mechanism, its advantages and disadvantages, It is also impossible to integrate it and achieve it at your fingertips. What's more, even a very complete class library cannot meet all business needs. When appropriate, we may have to write our own class library to support our business. In this series, let's learn about concurrent programming in depth and grow together. My ability is limited, if I have any mistakes, please correct me in time.

thread state

If you want to learn multi-threaded programming, you must first understand the state of the thread during the entire running period, and what mechanism is used to switch the thread between these states?

As shown in the figure above, the life cycle of a Java thread can be roughly described.

new state

In multithreaded programming, we usually use Threadthe Runnablekeyword to create threads, as follows:

 
  1. Thread thread = new Thread(new Runnable(){

  2.   @Override

  3.   public void run() {

  4.     System.out.println("thread");

  5.   }            

  6. });

When this thread is created, if you don't call it start()(or call it in other ways, such as in the thread pool submit()), it is no different from a normal object (an interviewer may test you here, at least I have experienced it). At this time, the OS scheduler will not allocate the time slicing required for CPU execution to it.

ready state

When a thread is created, it must enter the ready state before it can be allocated CPU time slices and can be scheduled by the operating system. Usually, we will makestart() the thread enter the ready state by calling, or the thread pool , as follows:submit()

 
  1. thread.start(); // 普通线程

  2. ExecutorService executor = Executors.newSingleThreadExecutor();// 创建线程池并将线程提交给线程池管理

  3. executor.submit(thread);

There are several ways for a thread to enter the ready state:

  • The thread enters the ready state from the new state, such as (start(), executor.submit())

  • The thread is restored from the running state to the ready state, (such as the executing thread is suspended, the CPU time slice runs out, yield())

  • The thread is restored from the blocked state to the ready state (for example, the sleep execution is completed, the join execution is completed)

  • The lock waiting in the lock pool has obtained the permission of the lock

Operating status

When a thread is in the ready state, it may be scheduled by the operating system at any time. The thread from the ready state to being scheduled by the operating system is called the thread entering the running state. The semantics are: the run()method is being scheduled by the operating system and is being executed.

blocking state

When the thread calls the sleep()or join()other methods from the running state, it will enter the blocking state, waiting for the execution time to expire, or join()the thread finishes executing, and the thread will automatically enter the ready state, as follows:

 
  1. try {

  2.  thread.sleep(1000);

  3. } catch (InterruptedException e) {

  4.  e.printStackTrace();

  5. }

  6. try {

  7.  thread.join();

  8. } catch (InterruptedException e) {

  9.  e.printStackTrace();

  10. }

Terminated/Dead State

When the thread executes the run()method , or is interrupted due to an exception, or interrupt()(in addition, you can still see some discarded items such as stop(),supend(), etc. ), the thread will enter the dead state. If the thread enters the dead state after executing the run()method , it does not mean that the object of the thread is recycled, and the thread can be awakened again.

A running thread enters a waiting queue

A running thread enters a waiting queue

When a running thread calls , wait()the thread loses the CPU time slice, enters a wait state, notify()/notifyAll()and wakes up waiting for other threads to call . If it is not awakened, the thread has been in a waiting state, which can also be called a suspended state. Pay attention to distinguish the thread calling sleep()into sleep state. The two are different. sleep()The semantics of the call is that the thread needs to enter the sleep state. The sleep time is already set. After the time is up, it will enter the ready state by itself and wait for the operating system to schedule. . wait()The purpose of the thread call is that the thread that owns the current lock object does not need to continue to execute (release the lock, give up the CPU time slice, and will not enter the ready state immediately), and the thread that enters the waiting queue needs to be notify()/notifyAll()woken up. After waking up, the thread will enter the lock pool to queue up and wait until it gets the permission to lock.

When we call wait()the , notify(), notifyAll()method, it can pass normally at compile time, but an exception may be thrown during runtime IllegalMonitorStateException, why is this? When the wait()statement , the precondition is that the current thread must have permission to lock the current object. Under normal circumstances, we usually say that before calling the wait()method , synchronizedthe object lock of the must be obtained [this statement itself is not correct, but it can explain the problem very well].

For example, when the thread join()method , if a careful student will check the source code, they will find wait(0)that the currently executing thread enters the waiting state by calling the method internally. The source code is as follows:

 
  1. public final synchronized void join(long millis)

  2.    throws InterruptedException {

  3.        // ....

  4.        if (millis == 0) {

  5.            while (isAlive()) {

  6.                wait(0);// 可以考虑wait()与wait(timeout)的区别

  7.            }

  8.        }

  9.        // ....

  10.    }

Enter the lock pool

When the executing thread synchronizedencounters , or the thread in the waiting queue is woken up notify(), notifyAll()the awakened thread will enter the lock pool, and will enter the ready state to continue execution until the lock is obtained. synchronizedNeedless to say, the keyword is a thread synchronization primitive, which is implemented by the built-in language. When a thread has entered the lock and has not completed the release, other threads execute here, and if they want to continue to execute, they need to enter the lock pool to queue up and wait for the current lock to be released until the lock is acquired (not necessarily on a first-come, first-served basis) to enter the ready state. As mentioned earlier, calling the wait()method needs to obtain the lock first, and its semantics are more likely (guessing): after the thread acquires the lock, during the execution process, it chooses to actively enter the waiting state because some conditions are not satisfied, until other threads It will wake up and be rescheduled by the operating system after doing some things. Therefore, only threads in the waiting queue can be called notify()to notifyAll()wake up.

 

The above is the author's understanding of the life cycle of threads. If there are any mistakes, please correct me!

                       

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325454404&siteId=291194637