There is only one way to implement threads
We know that threads can be started in at least four ways:
- Implement the Runnable interface
- Thread class inheritance
- Thread pools create threads
- Callable creation thread with return value
But there’s only one way to look at the bottom of them, which is through new Thread(), and the others are just wrapped on top of it.
Implementing the Runnable interface is better than inheriting the Thread class:
- The structure of the division of labor is more clear, the thread itself attributes and task logic decoupled.
- In some cases, the performance is better, and the task is handed over directly to the Thread pool without needing to new Thread() again.
- Better extensibility: implementation interfaces can be multiple, and inheritance can only be single.
Sometimes you might ask why the start thread is a start() method instead of a run() method. This question is very simple. Executing a run() method is actually executing a normal method of a class without starting a thread, while starting () method is a native method.
When we execute the start() method in Java, the underlying code calls the JVM’s c++ code Thread::start, and the c++ code calls the operating system’s create_thread. Wait for the CPU to schedule. There are many CPU scheduling algorithms, such as first come first service scheduling algorithm (FIFO), shortest first (that is, priority scheduling for short jobs), time slice rotation scheduling and so on. As shown below:
Thread state
There are six states in the life cycle of a thread in Java.
- NEW: Initial state, the thread is built, but the start method has not been called
- JAVA threads collectively refer to the ready and running states of an operating system as running
- BLOCKED: a state in which a thread enters a wait state, where the thread has given up CPU usage for some reason
- WAITING: indicates the WAITING state
- TIMED_WAITING: Timeout waiting state. After a timeout, the system automatically returns
- TERMINATED: indicates that the current thread is TERMINATED
Of course, this is not what I said, the source code is defined like this:
public enum State {
/** * Thread state for a thread which has not yet started. */
NEW,
/** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/** * Thread state for a terminated thread. * The thread has completed execution. */
TERMINATED;
}
Copy the code
Here are the six transitions:
New the newly created
New represents the state in which a Thread has been created but not started: when we create a Thread with New Thread(), the state is New if the Thread has not started calling the start() method. Once the thread calls start(), its state changes from New to Runnable.
Runnable Running status
The Runable state in Java corresponds to two operating system thread states, Running and Ready. That is, a thread in the Runnable state in Java may or may not be executing and is waiting for CPU resources to be allocated.
If a running thread is in a Runnable state, halfway through the task, the CPU executing the thread is scheduled to do something else, causing the thread to stop running temporarily, and its state remains the same as Runnable, because it may be scheduled back to continue executing the task at any time.
In Java, Blocked, Waiting and Timed Waiting are all referred to as Blocked states, which are shown below.
Blocked
As you can see from the above figure, the only way to get Blocked from the Runnable state is to enter synchronized protected code without snatching the Monitor lock, and the JVM places the current thread in the lock pool. When a Blocked thread grabs the Monitor lock, it returns from the Blocked state to the Runnable state.
The Waiting state
If we look at the figure above, there are three possibilities for a thread to enter a Waiting state.
- Without the object.wait () method with the Timeout parameter, the JVM queues the current thread.
- Thread.join() method with no Timeout parameter.
- LockSupport. Park () method.
Locksupport.park () is used in ReentrantLock and AQS source code.
Blocked differs from Waiting in that the Blocked thread is Waiting for another thread to release the monitor lock, whereas Waiting is Waiting for a condition, such as completion of the join thread, or notify()/notifyAll().
The Runnable state can be entered when locksupport.unpark () is executed, the join thread terminates, or is interrupted. When a notify() or notifyAll() is called to wake it up, it is immediately Blocked because a thread in a wake-waiting state can call notify() or notifyAll() and must already hold a Monitor lock. At this time, the thread in Waiting state will enter the Blocked state if it does not get the monitor lock. It is not able to snatch the lock until the thread that performs notify()/notifyAll() to wake it up completes and releases the monitor lock. If it can snatch the lock, It goes from Blocked to Runnable.
Timed Waiting state
Waiting is a Timed Waiting state. The two states are very similar. The only difference is that there is no time limit.
The following conditions cause a thread to enter a Timed Waiting state.
- Thread.sleep(Long millis) method with the time parameter set.
- Object. Wait (long timeout) method with time parameters set.
- Thread.join(Long millis) method with the time parameter set.
- Locksupport. parkNanos(long Nanos) with the time parameter set.
- Locksupport. parkUntil(long deadline) method.
The same is true for notify() and notifyAll() in a Timed Waiting, which are Blocked and return to the Runnable state after a successful lock grab. Of course, if it runs out of time and locksupport-unpark () is called, it will revert to the Runnable state without having to experience Blocked.
Terminated termination
Terminated state. There are two possible ways to enter the state.
- The run() method completes, and the thread exits normally.
- An exception that is not caught terminates the run() method, resulting in unexpected termination.
The threadstop interrupt
We know that Thread provides methods to manipulate threads, such as stop(), suspend(), and resume(), which are marked by Java as @deprecated, indicating that these methods are Deprecated.
Because stop() stops the thread directly, it doesn’t have enough time to process the logic that wants to save data before stopping, and the task comes to a screeching halt, which can lead to data integrity issues. This behavior is similar to executing kill -9 on Linux, which is an unsafe operation.
The problem with suspend() and resume(), however, is that if the thread calls suspend(), it does not release the lock and begins to sleep, but it may still hold the lock. This can lead to deadlock problems because the lock is held before the thread is resumed (). Is not going to be released.
interrupt
The correct way to stop a thread is to use an interrupt, but the interrupt simply notifies the stopped thread. For the stopped thread, it has full autonomy, it can choose to stop immediately, after a period of time, or choose not to stop at all.
Here’s an example:
public class InterruptExample implements Runnable {
// Interrupt is equivalent to defining a volatile variable
//volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new InterruptExample());
t1.start();
Thread.sleep(5);
// The Main thread decides to stop the T1 thread, sends an interrupt signal, and the interrupt flag becomes true
t1.interrupt();
}
@Override
public void run(a) {
while(! Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() +"--"); }}}Copy the code
Run it, run it for a while and then stopAfter the main thread calls T1 interrupt(), the thread’s interrupt bit is set to true. Each thread has this flag bit, which is checked periodically as the thread executes. If the flag bit is set to true, it indicates that a program wants to terminate the thread. In the body statement of the while loop, thread.currentThread ().isinterrupt () is used to determine whether the Thread is interrupted. If true, the loop is broken and the Thread is terminated.
A thread interrupts in a blocked state
Let’s look at the second example that adds Thread.sleep 1000 seconds to the loop.
public class InterruptSleepExample implements Runnable {
// Interrupt is equivalent to defining a volatile variable
//volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new InterruptSleepExample());
t1.start();
Thread.sleep(5);
// The Main thread decides to stop the T1 thread, sends an interrupt signal, and the interrupt flag becomes true
t1.interrupt();
}
@Override
public void run(a) {
while(! Thread.currentThread().isInterrupted()) {try {
Thread.sleep(1000000);
} catch (InterruptedException e) {// The interrupt flag becomes false
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--"); }}}Copy the code
Let’s look at the run result. It’s stuck. It doesn’t stop. This is because the main thread calls t1.interrupt() while T1 is sleeping. It will not receive the interrupt signal until the sleep is over. This interruption is too late, I let you interrupt, you are still in the silly sleep.
The designers of Java development have taken this into account. Methods such as sleep, wait, etc., allow threads to enter a blocking method that causes them to sleep, while the dormant thread is interruptedSenses an interrupt signal and throws an InterruptedException, clears the interrupt signal and sets the interrupt flag bit to false.
There are several ways to do this:
- Catch the exception without handling it, e.prinintStackTrace (); Print the information
- Throws an exception outwards, meaning throws InterruptedException on the method
- Interrupt again, the code is as follows, adding thread.currentThread ().interrupt();
@Override
public void run(a) {
while(! Thread.currentThread().isInterrupted()) {try {
Thread.sleep(1000000);
} catch (InterruptedException e) {// The interrupt flag becomes false
e.printStackTrace();
// Change the interrupt flag to true
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + "--"); }}Copy the code
The thread senses this, and we artificially change the interrupt flag to true to stop the thread. Interrupts are rarely used when dealing with threads because most of the time we use thread pools. Thread pools have been wrapped up for me, but this is something I need to know. Thanks for watching and give it a thumbs up