This article has been included in Github: github.com/JavaLiuTong…. For more dry goods articles, pay attention to the official account: Classmate Liu who can’t speak
Three ways to create threads
Inherit Thread
We want to simply start a thread in the code. The most familiar way is to inherit Thread. This class is a thread class provided by JDK. This class implements the Runnable interface. Let’s take a look at some of the more commonly used methods of Thread
The first is its construction method
For the construction method, we only need to focus on the first two, one is the no-argument construction, and the other is the implementation class passed in Runnable
Other more commonly used methods:
public synchronized void start() : start the thread
public void run(): The method specifically executed by the thread, which needs to be rewritten in the inherited class
public static native void sleep(long millis): The thread sleeps, and the sleep time needs to be passed in. This method will suspend the thread execution until the thread sleep time ends
public static void sleep(long millis, int nanos): Same as the previous method, except that an additional time mechanism is passed
public void interrupt(): Mark the thread with an interrupt flag
public boolean isInterrupted(): Determine whether the thread is interrupted
public final synchronized void setName(String name): Set a name for the thread
public final void join(): Let the main thread wait for the execution of the child thread to end
public static native void yield(): Indicates that the current thread is willing to give up CPU usage rights
I will not explain the specific use of the method here one by one, let’s take a look at the implementation details of the code
Sample code:
// custom class inherits Thread public class Thread01 extends Thread{<!-- --> @Override public void run() {<!-- --> System.out.println("Hello World"); } }
public static void main(String[] args) throws Exception {<!-- --> Thread01 thread01 = new Thread01(); // start the thread thread01.start(); }
Implement the Runnable interface
Runnable internally provides a run abstract method. I also said above that Thread implements the Runnable interface
Because Runnable does not provide us with a method to start a thread, we need to use the Thread class to start it
The code example is as follows:
// Custom class implements Runnable interface public class Thread02 implements Runnable {<!-- --> @Override public void run() {<!-- --> System.out.println("Hello World"); } }
public static void main(String[] args) throws Exception {<!-- --> Thread02 thread02 = new Thread02(); // start with Thread class Thread thread = new Thread(thread02); // start the thread thread. start(); }
Implement the Callable interface
The above two ways to create threads have a disadvantage, but the results of thread execution cannot be obtained. We can obtain the results of thread execution by implementing the Callable interface
Similarly, Callable only provides one method, and the interface receives a generic type as the returned result parameter
Callable also does not provide a method to start a thread, so we also need to use the Thread class to start
But we can see that Thread does not provide a construction method for Callable parameter passing in addition to the construction method of Runnable parameter passing, so we cannot start it directly through Thread, we need a medium, this medium is FutureTask, in fact, we can find out carefully , Callable and FutureTask are both under the JUC concurrency package
FutureTask inherits the Runnable interface, so FutureTask itself is an implementation of Runnable
FutureTask also provides two construction methods, one is Callable parameter passing, the other is Runnable parameter passing
So we can wrap the implementation of Callable into a FutureTask, and then start it through Thread
Sample code:
// Custom Callable implementation public class Thread03 implements Callable<String> {<!-- --> @Override public String call() throws Exception {<!-- --> return "This is the result of a thread execution..."; } }
public static void main(String[] args) throws Exception {<!-- --> Thread03 thread03 = new Thread03(); // wrapped into FutureTask FutureTask futureTask=new FutureTask(thread03); // start with Thread class Thread thread = new Thread(futureTask); thread. start(); // Get the execution result System.out.println(futureTask.get()); }
Thread lifecycle
Just like Spring’s beans, threads also have a life cycle
Java’s thread life cycle has six states:
NEW (initialization state)
RUNNABLE
BLOCKED (blocked state)
WAITING (no time limit waiting)
TIMED_WAITING (timed_waiting)
TERMINATED
Among them, the three states of BLOCKED, WAITING, and TIMED_WAITING can be attributed to one state, that is, the dormant state, because as long as these three states are one of them, the thread will never have the right to use the CPU.
Let’s take a look at how to transition between thread states
1. From NEW to RUNNABLE state
The Thread object just created by Java is in the NEW state. At this time, the JVM allocates memory for it and initializes the value of its member variables. The thread will not execute the thread execution body.
When the start() method is called, it will transition from NEW to RUNNABLE state
2. RUNNABLE to BLOCKED state
When a thread calls a synchronized modified method or code block, other threads can only wait, and the waiting thread will transition from RUNNABLE to BLOCKED state
When the waiting thread acquires the synchronized implicit lock, it will transition from BLOCKED to RUNNABLE state
3. From RUNNABLE to WAITING state
There are three scenarios that trigger transitions between these two states
In the first scenario, the thread that acquires the synchronized implicit lock calls the Object.wait() method without parameters
In the second scenario, call the Thread.join() method without parameters. If there is a thread ThreadA, when ThreadA.join() is called, the thread that executes this statement will wait for ThreadA to finish executing, and during the waiting process , the state will switch from RUNNABLE to WAITING, and when ThreadA finishes executing, the waiting thread will switch from WAITING to RUNNABLE
The third scenario is to call the LockSupport.park() method. When the thread calls this method, the state will change from the RUNNABLE state to the WAITING state. Calling LockSupport.unpark() can wake up the target thread, and the target thread state will change from the WAITING state to RUNNABLE state
4. RUNNABLE to TIMED_WAITING state
There are five scenarios for this state transition
In the first scenario, call the Thread.sleep(long millis) method with a timeout parameter
In the second scenario, the thread that acquires the synchronized implicit lock calls the Object.wait(long timeout) method with a timeout parameter
In the third scenario, call the Thread.join(long millis) method with a timeout parameter
In the fourth scenario, call the LockSupport.parkNanos(Object blocker, long deadline) method with a timeout parameter
In the fifth scenario, call the LockSupport.parkUntil(long deadline) method with a timeout parameter
The difference between TIMED_WAITING and WAITING states is that there is an additional timeout parameter
5. The difference between RUNNABLE and WAITING states
After the thread executes the run() method, it will automatically switch to the TERMINATED state. Of course, if an exception is thrown when the run() method is executed, the thread will also be terminated
Interruption strategy for threads
We may sometimes terminate the running of threads, such as some middleware, which may need to terminate some running threads during normal shutdown
Thread provides a stop() method, which will terminate the thread directly without giving any chance to breathe
But there is also a problem with such a violent termination of the thread, that is, if the thread is performing some tasks, it happens that the thread holds the ReentrantLock lock. Once we terminate the thread through violence, the thread will not automatically call the unlock() of ReentrantLock to Release the lock, then the lock will never be released, so the JDK official does not recommend using the stop method to terminate the thread, and this method is also marked as obsolete
Thread provides us with an interrupt() method, which is relatively more elegant
When this method is called, the thread will not be terminated, but a termination mark will be marked on the thread to mark it as a terminated thread. In subsequent processing, we only need to call the isInterrupted() method to determine whether the thread is terminated Thread, if it is, we will do termination processing, and we can also ignore this termination mark
In addition to calling isInterrupted() to actively detect, the other is to detect through exceptions
If we call wait(), join(), sleep() methods, an InterruptedException will be thrown. The trigger condition of this exception is: other threads call the interrupt() method of this thread
Communication between threads
Threads are not completely independent of each other, and some kind of communication mechanism is needed to achieve the purpose of mutual assistance
Object provides us with three methods: wait(), notifyAll(), and notify(). When a thread calls the wait() method, it will enter the waiting process. Calling notifyAll() and notify() will wake up the waiting thread.
The difference between notifyAll() and notify() is:
notify() wakes up a single thread that is waiting on this object’s monitor. If there are multiple threads waiting, choose one of them to wake up randomly (determined by the scheduler), and the awakened thread has the right to compete for resources fairly
notifyAll() wakes up all threads that are waiting for this object monitor, and all awakened threads compete for resources fairly
Let’s illustrate this with a code example
public class Thread01 extends Thread{<!-- --> private Object lock; public Thread01(Object lock){<!-- --> this. lock = lock; } @Override public void run() {<!-- --> synchronized(lock){<!-- --> try {<!-- --> // Call the wait method, the thread enters waiting lock. wait(); } catch (InterruptedException e) {<!-- --> e.printStackTrace(); } System.out.println(super.getName() + "was awakened..."); } } }
public static void main(String[] args) throws Exception {<!-- --> Object lock = new Object(); Thread01 thread01 = new Thread01(lock); thread01.setName("Thread 1"); // After starting the thread, the thread will be blocked and wait thread01.start(); Thread. sleep(3000); synchronized (lock){<!-- --> // wake up the waiting thread lock. notify(); } }
I have explained the specific details in the comments. If you are interested, you can run this code yourself.
This communication mechanism has disadvantages, so it is rarely used. JUC provides a LockSupport class. This class provides two methods, part() and unpark(), which correspond to thread blocking and wake-up respectively. Generally, this method is used to make threads Communication
Conclusion
The code word is not easy, and I hope you can like it more, collect it and support it