Location>code7788 >text

Thread state transitions? How many ways to create a thread? How are threads stopped?

Popularity:707 ℃/2024-09-25 00:18:27

Thread state transition

New

NEW: Initial state, the thread is constructed, but the start() method has not been called yet.

Runnable

RUNNABLE: Runnable state, which can include: running state and ready state. That is, it may be running or waiting for a CPU time slice.

Contains the operating system thread states Running and Ready.

Blocking

Waits to acquire an exclusive lock and ends this state if its thread releases the lock.

 new Thread(new BlockedDemo(),"Blocked-Demo-1").start();
 new Thread(new BlockedDemo(),"Blocked-Demo-2").start();

static class BlockedDemo extends Thread {
    @Override
    public void run() {
        while (true) {
            synchronized () {
                try {
                    (100);
                } catch (InterruptedException e) {
                    ();
                }
            }
        }
    }
}

Waiting for an indefinite period of time

Wait for other threads to explicitly wake up, otherwise no CPU time slice will be allocated.

Access Methods Withdrawal methods
() method without Timeout parameter () / ()
() method without Timeout parameter Execution of the called thread is complete
() method -
new Thread(() -> {
    while (true) {
        synchronized () {
            try {
                ();
            } catch (InterruptedException e) {
                ();
            }
        }
    }
},"Waiting-Demo").start();

Timed Waiting

There is no need to wait for other threads to wake up explicitly, they will be woken up automatically by the system after a certain period of time.

Calling the () method to put a thread into a deadline waiting state is often described as "putting a thread to sleep".

A call to the () method that causes a thread to wait for a specified period of time, or indefinitely, is often described as "hanging a thread".

Sleep and hang are used to describe behavior, while blocking and waiting are used to describe state.

The difference between blocking and waiting is that blocking is passive, it is waiting to acquire an exclusive lock. Waiting, on the other hand, is active and is accessed by calling methods such as () and ().

Access Methods Withdrawal methods
() method end of time
() method with Timeout parameter set End of time / () / ()
() method with Timeout parameter set End of time / Execution of the called thread is finished.
() method -
() method -
new Thread(() -> {
    while (true) {
        try {
            (100);
        } catch (InterruptedException e) {
            ();
        }
    }
}, "Time-Waiting-Demo").start();

Terminated

It can either end itself after the thread finishes its task, or it can end when an exception is thrown.

Several ways to create a thread

There are three ways to use threads.

  • Implements the Runnable interface;

  • Implements the Callable interface;

  • Inherits the Thread class.

  • Using Thread Pools

Classes that implement the Runnable and Callable interfaces can only be treated as a task that can be run in a thread, not a thread in the true sense of the word, and therefore need to be called by a Thread at the end. You can say that the task is thread-driven and thus executed.

Inherit Thread class

It is also necessary to implement the run() method because the Thread class implements the Runable interface.

When the start() method is called to start a thread, the virtual machine places the thread in a ready queue and waits for it to be dispatched, and when a thread is dispatched, it executes the run() method of that thread.

public class MyThread extends Thread {
    public void run() {
        // ...
    }
}
public static void main(String[] args) {
   MyThread mt = new MyThread();
   ();
}

Implement the Runnable interface

The run() method needs to be implemented.

Threads are started by Thread calling the start() method.

public class MyRunnable implements Runnable {
    public void run() {
        // ...
    }
}
public static void main(String[] args) {
    MyRunnable instance = new MyRunnable();
    Thread thread = new Thread(instance);
    ();
}

Implementing the Callable Interface

In contrast to a Runnable, a Callable can have a return value, which is encapsulated by a FutureTask.

public class MyCallable implements Callable<Integer> {
    public Integer call() {
        return 123;
    }
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
    MyCallable mc = new MyCallable();
    FutureTask<Integer> ft = new FutureTask<>(mc);
    Thread thread = new Thread(ft);
    ();
    (());
}

Or you can use a thread pool for execution

MyCallable myCallable = new MyCallable();
ExecutorService executorService = (1);
Future<String> submit = (myCallable);
(());

Implementing an Interface vs. Inheriting Thread

Implementing the interface would be better because:.

  • Java does not support multiple inheritance, so if you inherit from the Thread class, you can't inherit from other classes, but you can implement multiple interfaces;

  • Classes may only need to be executable, and inheriting the entire Thread class would be overkill.

Basic Threading Mechanism

Daemon

Guardian threads are threads that provide services in the background while the program is running and are not an integral part of the program.

The program terminates when all non-daemon threads end, killing all daemon threads as well.

main() is a non-guarded thread. So if you set this, you can make the child thread exit as the main thread exits

Use the setDaemon() method to set a thread as a daemon.

public static void main(String[] args) {
    Thread thread = new Thread(new MyRunnable());
    (true);
}

sleep()

The (millisec) method sleeps the currently executing thread. millisec is measured in milliseconds.

Sleep's workflow:

  1. Hanging a thread and modifying its running state
  2. Setting a timer with the parameters provided by sleep()
  3. When the time expires the timer is triggered and the kernel receives the interrupt and modifies the running state of the thread. Ready queue for scheduling e.g. threads are marked as ready and enter the ready queue for scheduling.

sleep() may throw an InterruptedException, which must be handled locally because exceptions cannot be propagated back to main() across threads. Other exceptions thrown in the thread also need to be handled locally.

public void run() {
    try {
        (3000);//doesn't mean that it will definitely be executed in 3 seconds, because CPU resources are not necessarily allocated to this thread at this point in time
    } catch (InterruptedException e) {
        ();
    }
}

The beauty of sleep(0)

In the thread, calling sleep(0) frees up cpu time, allowing the thread to immediately rejoin the ready queue instead of the wait queue, and sleep(0) frees up the slice of time remaining in the current thread (if there is any left), which allows the operating system to switch to other threads for execution, improving efficiency.

(0) doesn't really mean that the thread hangs for 0 milliseconds, it means that the current thread that called (0) is indeed frozen for a bit, giving other threads a chance to prioritize their execution. (0) is your thread temporarily abandoning the cpu, i.e., releasing some unused time slice for other threads or processes to use, which is equivalent to a yielding action. It's actually equivalent toUsage of yield

yield()

A call to the static method () declares that the current thread has completed the most important part of its lifecycle and can be switched to another thread for execution. The method is only a suggestion to the thread scheduler, and only suggests that other threads with the same priority can run.

public void run() {
    ();
}

Thread Stop

Stop method

The stop method stops the thread, but it is a deprecated method that is not recommended, as can be seen in the source code of the Thread class, which looks like this:

The stop method is a non-recommended expired method modified by @Deprecated, and the first sentence of the comment states that the stop method is non-safe.

The reason for this is that it forcibly interrupts the execution of a thread when it terminates, regardless of whether or not the run method has finished executing, and releases any lock objects held by the thread. This is seen by other threads that are blocked by the lock request, causing them to continue down the line. This causes data inconsistencies.

For example, bank transfers, from account A to account B transfer $ 500, the process is divided into three steps, the first step is to subtract $ 500 from account A. If the thread is stopped at this point, the thread will release the lock it obtained, and then other threads continue to execute, so that the A account is inexplicably less than $ 500 and the B account did not receive the money. This is the insecurity of the stop method.

Set Flag Bits

If the thread's run method is executing a loop that repeats itself, you can provide a flag to control whether the loop continues or not

class FlagThread extends Thread {
    // Custom Interrupt Identifiers
    public volatile boolean isInterrupt = false;
    @Override
    public void run() {
        // in case of true -> disrupt execution
        while (!isInterrupt) {
            // Business Logic Processing
        }
    }
}

But the problem with custom interrupt identifiers is that threads are not interrupted in time. Because the thread can not call while(!isInterrupt) to determine whether the thread is terminated or not during execution, it can only determine whether to terminate the current thread in the next round of running, so it is not timely enough to interrupt the thread, such as the following code:

class InterruptFlag {
    // Customized interrupt identifier
    private static volatile boolean isInterrupt = false; // Customized interrupt identifier.

    public static void main(String[] args) throws InterruptedException {
        // Create an interruptible thread instance
        Thread thread = new Thread(() -> {
            while (!isInterrupt) { // stop thread if isInterrupt=true
                ("thread Execution Step 1: Thread is about to go to sleep");;
                try {
                    // Hibernate for 1s
                    (1000); } catch (InterruptedException e.g.)
                } catch (InterruptedException e) {
                    (); } catch (InterruptedException e) {
                }
                ("thread Executed Step 2: Thread executed task"); }
            }
        }); }
        (); // Start the thread

        // Dormant for 100ms, waiting for thread to get up and running
        (100); ("Main thread: attempt to terminate thread thread"); // Start thread.
        ("Main thread: attempting to terminate thread thread");; // Change the interrupt identifier to interrupt the thread.
        // Change the interrupt identifier to interrupt the thread
        isInterrupt = true; }
    }
}

Output: What we expect is that after the thread executes step 1, it receives an instruction to interrupt the thread and then stops executing step 2. However, as we can see from the above execution result, we can't achieve the expected result by using a customized interrupt identifier, and this is the problem of the customized interrupt identifier, which doesn't respond in a timely enough manner.

interrupted

This approach requires a while loop to determine the use of the

Using the interrupt method, you can send an interrupt instruction to the thread executing the task. It does not interrupt the thread directly, but sends a signal to interrupt the thread, giving the code writer the initiative to decide whether or not to interrupt the thread. It can receive the interrupt instruction in a more timely manner than a custom interrupt identifier, as shown in the following code:

public static void main(String[] args) throws InterruptedException {
    // Create an interruptible thread instance
    Thread thread = new Thread(() -> {
        while (! ().isInterrupted()) {
            ("thread Execute Step 1: Thread is about to go to sleep"); {
            try {
                // Hibernate for 1s
                (1000); } catch (InterruptedException e.g.
            } catch (InterruptedException e) {
                ("thread thread received interrupt command, performing interrupt operation"); // Interrupt current thread task.
                // Interrupt the current thread's task execution
                break; }
            }
            ("thread Executed step 2: thread executed task"); }
        }
    }); // Start the thread.
    (); // Start the thread

    // Dormant for 100ms, waiting for thread to get up and running
    (100); ("Main thread: attempt to terminate thread thread"); // Start thread.
    ("Main thread: attempting to terminate thread thread");; // Change the interrupt identifier to interrupt the thread.
    // Change the interrupt identifier to interrupt the thread
    (); // Modify the interrupt identifier to interrupt the thread.
}

Output:

As you can see from the above results, the thread is interrupted immediately after receiving the interrupt instruction, which provides a more timely response to the interrupt thread instruction than the previous method of customizing the interrupt identifier.

Utilizing interruptedException

This approach does not require a while loop to determine the use of the

If a thread enters a blocking state due to the execution of join(), sleep or wait(), and you want to stop it, you can call interrupt(), and the program will throw an interruptedException. You can use this exception to terminate the thread

public void run() {
    (() + "start");
    int i=0;
    //while (!()){
    while (!().isInterrupted()){
        try {
            (10000);
        } catch (InterruptedException e) {
            //();
            ("Interrupt Threads");
            break;//Interrupt by recognizing an exception
        }
        (() + " "+ i);
        i++;
    }
    (() + "end");
}

Executor interrupt operations

Calling the Executor's shutdown() method waits until all threads have finished executing before shutting down, but calling the shutdownNow() method is equivalent to calling each thread's interrupt() method.

The following thread creation using Lambda is equivalent to creating an anonymous internal thread.

public static void main(String[] args) {
    ExecutorService executorService = ();
    (() -> {
        try {
            (2000);
            ("Thread run");
        } catch (InterruptedException e) {
            ();
        }
    });
    ();
    ("Main run");
}
Main run
: sleep interrupted
    at (Native Method)
    at $main$0(:9)
    at ExecutorInterruptExample$$Lambda$1/(Unknown Source)
    at (:1142)
    at $(:617)
    at (:745)

If you want to interrupt only one thread in the Executor, you can submit a thread by using the submit() method, which returns a Future<? > object, which can be interrupted by calling the cancel(true) method of that object.

Future<?> future = (() -> {
    // ..
});
(true);

Collaboration between threads

When multiple threads can work together to solve a problem, the threads need to be coordinated if some parts must be completed before others.

join()

case (law)

Calling the join() method of another thread in a thread hangs the current thread instead of waiting busy until the target thread finishes.

For the following code, even though thread b starts first, since thread b calls thread a's join() method in thread b, thread b waits for thread a to finish before it continues execution, so the output of thread a is guaranteed to precede the output of thread b.

public class JoinExample {

    private class A extends Thread {
        @Override
        public void run() {
            ("A");
        }
    }

    private class B extends Thread {

        private A a;

        B(A a) {
             = a;
        }

        @Override
        public void run() {
            try {
                ();
            } catch (InterruptedException e) {
                ();
            }
            ("B");
        }
    }

    public void test() {
        A a = new A();
        B b = new B(a);
        ();
        ();
    }
}
public static void main(String[] args) {
    JoinExample example = new JoinExample();
    ();
}
A
B

principle

public final synchronized void join(long millis)
throws InterruptedException {
    long base = ();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {//Check if the thread is alive,As long as the thread is still open.,The main thread will just keep blocking
            wait(0);//Here.waitCalled Local Methods。
        }
    } else {//Wait for a specified period of time
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = () - base;
        }
    }
}

Looking at the source code, the join method actually calls the wait method to block the thread until it finishes running. Notice that the join method is preceded by the synchronized modifier, which is equivalent to:

public final void join(long millis){
 synchronized(this){
        //code block
    }
}

That is to say, the object of the lock, that is, the object of the thread that calls this lock, in the main() method, that is, t1, holding this lock is the main thread, that is, the main() method, that is to say, the code is equivalent to the following:

// Code before ()
synchronized (t1) {
 // The caller thread enters t1's waitSet and waits until t1 finishes running.
 while (()) {
  (0); }
 }
}
// The code after ().

Also as a result the main thread goes into a wait queue until the t1 thread finishes.

Here may be a lot of people will be puzzled, why, blocking is not t1, but the main thread?

In fact, if you want to block t1, then you should block in the run method of t1, such as writing wait() in the run method; (of course, there is also the suspend method, which belongs to the non-Java level, another story)

When the wait method is called, it allows the thread holding the lock to enter the wait queue, which is called by the main thread, so the t1 thread is not blocked; it is the main thread that is blocked.

That is, the join method is a synchronized method, and when the main thread calls the () method, the main thread first acquires a lock on the t1 object, and then enters the method and calls the wait() method on the t1 object, which puts the main thread into the waiting pool for the t1 object.

So the problem is that only the wait method is seen here, but not the notify or notifyAll methods, so where does the main thread get woken up?

Refer to the jvm code here:

static void ensure_join(JavaThread* thread) {

 Handle threadObj(thread, thread->threadObj());

 ObjectLocker lock(threadObj, thread);

 hread->clear_pending_exception().

 //TERMINATED in this sentence means that this is run after the thread has ended
 java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);

    // Here it will be clear that native threads, the isAlive() method will return false
    java_lang_Thread::set_thread(threadObj(), NULL);

 //thread is the current thread, call this method to wake up the waiting thread.
 lock.notify_all(thread);

 hread->clear_pending_exception().

}

Actually it is the existence of the method lock.notify_all(thread) in the jvm virtual machine, which is called after the t1 thread has finished and finally wakes up the main thread.

So to simplify, the process i.e:

wait() notify() notifyAll()

Calling wait() causes a thread to wait for a condition to be met. The thread hangs while it waits, and when other threads run so that the condition is met, the other threads call notify() or notifyAll() to wake up the hung thread.

They are all part of an Object, not a Thread.

Can only be used in synchronized methods or synchronized control blocks, otherwise an IllegalMonitorStateExeception will be thrown at runtime.

During a hang with wait(), the thread releases the lock. This is because if the lock is not released, then other threads will not have access to the object's synchronization methods or synchronization blocks, and will not be able to perform notify() or notifyAll() to wake up the pending thread, resulting in a deadlock.

public class WaitNotifyExample {
    public synchronized void before() {
        ("before");
        notifyAll();
    }

    public synchronized void after() {
        try {
            wait();
        } catch (InterruptedException e) {
            ();
        }
        ("after");
    }
}
public static void main(String[] args) {
    ExecutorService executorService = ();
    WaitNotifyExample example = new WaitNotifyExample();
    (() -> ());
    (() -> ());
}
before
after

The difference between wait() and sleep()

  • wait() is a method of Object, while sleep() is a static method of Thread;

  • wait() releases the lock, sleep() does not.

await() signal() signalAll()

The library provides the Condition class to realize the coordination between threads, you can call await() method on the Condition to make the thread wait, and other threads call signal() or signalAll() method to wake up the waiting thread. Compared to wait(), await() is more flexible because it can specify the conditions for waiting.

Use Lock to get a Condition object.

public class AwaitSignalExample {
    private Lock lock = new ReentrantLock();
    private Condition condition = ();

    public void before() {
        ();
        try {
            ("before");
            ();
        } finally {
            ();
        }
    }

    public void after() {
        ();
        try {
            ();
            ("after");
        } catch (InterruptedException e) {
            ();
        } finally {
            ();
        }
    }
}
public static void main(String[] args) {
    ExecutorService executorService = ();
    AwaitSignalExample example = new AwaitSignalExample();
    (() -> ());
    (() -> ());
}
before
after

Exception handling in threads

How exceptions are swallowed in Runnable

Runnable interfacesrun() methods are not allowed to throw any checked exceptions and can only handle or throw runtime exceptions (unchecked exceptions). When throwing an exception in therun() When exceptions occur within a method, if they are not explicitly caught and handled, they will usually be caught during the execution of thatRunnable The exception causes the thread to terminate, but does not affect the execution of other threads.

public void uncaughtException(Thread t, Throwable e) {
   if (parent != null) {
        (t, e);
   } else {
         ueh =
            ();
        if (ueh != null) {
            (t, e);
        } else if (!(e instanceof ThreadDeath)) {
            ("Exception in thread \""
                             + () + "\" ");
            ();
        }
    }
}

Solution:

  1. Caught exceptions displayed in the run method

    public void run() {
        try {
            // Code that may throw an exception
        } catch (Exception e) {
            // Log or handle the exception
            throw new RuntimeException(e); }
        }
    }
    
  2. Set the created thread with aUncaughtExceptionHandler

    Thread t = new Thread(() -> {
       int i = 1 / 0;
    }, "t1");
    (new () {
       @Override
       public void uncaughtException(Thread t, Throwable e) {
            ('---', e);
       }
    });
    
  3. utilizationCallablesubstitute (X for Y, or a number in an algebraic expression)RunnableCallable(used form a nominal expression)callmethod allows for exceptions to be thrown, which can then be thrown by submitting them to theExecutorServicereturnedFutureto catch and handle these exceptions

    ExecutorService executor = (1);
    Future<? > future = (() -> {
        // Code that might throw an exception
    });
    
    try {
        (); // This catches the exception in the Callable.
    } catch (ExecutionException e) {
        Throwable cause = (); // Get the original exception.
    }
    

How exceptions are swallowed in Callable

class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        ("===> commencecallable");
        int i = 1 / 0; //anomaly
        return "callableresults";
    }
}

public class CallableAndRunnableTest {

    public static void main(String[] args) {
        (" =========> main start ");
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 1, , new ArrayBlockingQueue<>(100));
        Future<String> submit = (new MyCallable());
        try {
            (2);
        } catch (InterruptedException e) {
            ();
        }
        (" =========> main end ");
    }
}

running result

 =========> main start
 ===> start callable
 =========> main end

The source code is below:

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

RunableFuture<T> It's an interface, but it inherits the Runnable interface, and the implementation class is FutureTask, so it's important to see if the run method in FutureTask is related to the Callable in the constructor:

public void run() {
     // The case where the state is not the initial state,No subsequent logical processing
     // That would berun method can only be executed once
     if (state != NEW ||
        !(this, runnerOffset,
                                   null, ()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            //
            boolean ran;
            try {
                // fulfillment Callable inner call methodologies ,Deposit the results inresultvariable
                result = ();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                 // call methodologies异常 , Record the abnormal results
                setException(ex);
            }
            // call methodologies正常fulfillment完毕 ,Perform results storage
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

The next step is to see if storing the normal results ofset(result)method and thesetException(ex) methodologies

protected void setException(Throwable t) {
    if ((this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        (this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion();
    }
}

protected void set(V v) {
    if ((this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        (this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

Both of these codes do an operation that takes the normal resultresult and abnormal resultsexception are assigned to theoutcome This variable .

Let's look at the get method of future.

// Here you have to look at the end of the Task's state, if the end of the normal state, the state is NORMAL, abnormal results, the state is EXCEPTIONAL. Look at the definition of several states, as follows:
private volatile int state;
private static final int NEW = 0; private static final int COMPLETE; private static final int NORMAL
private static final int COMPLETING = 1;
private static final int NORMAL = 2;





/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    // NORMAL(2) and EXCEPTIONAL(3) are all greater than COMPLETING(1), so the if will not go away when the Task ends.
    if (s <= COMPLETING)
         s = awaitDone(false, 0L); // Key point: return result.
    // Focus: return the result
    return report(s); }
}

private V report(int s) throws ExecutionException {
    // The previous normal results or exceptions are stored in the Object outcomme.
    Object x = outcome; } private V report(int s)
    // Returns normal
    if (s == NORMAL)
        return (V)x; // EXCEPTIONAL(V); if (s == NORMAL)
    // EXCEPTIONAL(3) is smaller than CANCELLED(4), so it doesn't go to that if branch, and goes straight to the subsequent logic of throw.
    if (s >= CANCELLED)
        throw new CancellationException();
    // not equal to NORMAL and greater than or equal to CANCELLED, combined with the state filtering done before calling report(int s ).
    // At this point, it can only be EXCEPTIONAL(3).
    throw new ExecutionException((Throwable)x);
}

So you can get the exception result via the get method

About the Author.

From the first-line programmer Seven's exploration and practice, continuous learning iteration in the~

This article is included in my personal blog:https://

Public number: seven97, welcome to follow~