Try to explore all aspects of Thread

preface

In the following period of time, we will explore the contents related to threads, including various locks. The summary of threads is as follows:

The thread has priority. The thread with high priority has priority over the thread with low priority. When a new thread is created in a thread, the priority of the new thread is set to the priority of the current thread; When the JVM starts, there is a non daemon thread by default (calling the main method of a class); Threads can be marked as guardian threads, and each thread can specify a name. If it is not specified, the bottom layer will generate a new name for it; There are two ways to create threads, as follows:

    // Only one thread can be created when there is only one instance
    class TestA extends Thread {

        @Override
        public void run() {
            System.out.println("TestA");
        }
    }

    // It is more common to create multiple threads when there is only one instance
    class TestB implements Runnable {

        @Override
        public void run() {
            System.out.println("TestB");
        }
    }

All aspects

Exploring the source code of Thread is based on jdk1 8. Understand the status of the next Thread before exploring.

  • NEW: creates a NEW object, but has not started calling the start method.

  • RUNNABLE: after the start method is called, the thread is in the ready state, which is simply not running. When the thread scheduler allocates the time slice to the thread, it becomes the running state.

  • BLOCKED: wait for the object lock on the synchronization control method or code block because the object lock has been occupied by other threads. When a thread is BLOCKED, the thread scheduler ignores the thread and does not allocate any CPU time. It is not possible for the thread to perform operations until it enters the ready state.

  • WAITING: WAITING for another thread to perform specific operations, such as wake-up (notification) or termination. It will be in this state after calling Object#wait, Thread#join and LockSupport#park.

  • TIMED_WAITING: this state is somewhat similar to waiting, except that it is a wait within a limited time, that is, when another thread does not perform a specific operation, the thread will become this state after a specified time (the lock is not obtained at this time), and will become a runnable state after the lock is obtained (the transition of this state is personal conjecture, and the Java specification and comments do not specify it), Calling Thread#sleep(long), Object#wait(long), Thread#join(long), LockSupport#parkUntil may be in this state. By the way, if the parameters in the method are all 0, it is equivalent to calling the wait method directly, and the state at this time will be waiting

  • TERMINATED: thread execution is completed, which refers to the return from the run method.

Briefly describe the following important concepts, which are extracted from the Java specification.

Object#wait, Object#notify and Object#notify can only be used in synchronization control methods or synchronized code blocks. Otherwise, even if they can be compiled, IllegalMonitorStateException will be thrown at run time. Because these methods operate locks when calling, they must obtain locks first. Thread\sleep and thread\yield can be called in asynchronous control methods because they never release locks, let alone operations.

For the Object#wait(long millisecs, int nanosecs) method, note that the nanosecs parameter should be in the range of 0-999999 (the conversion between milliseconds and nanoseconds is 6 zeros), and millisecs cannot be negative.

We all know that each Object has an associated monitor, which is commonly referred to as a lock. In addition, it also has an associated waiting container. It is easy to understand that what is stored in it is the thread in the waiting state (slightly tangled, the thread in the blocking state will not be in the container because it is related to the monitor), that is, after the current thread calls the Object#wait method, At this time, the number of threads waiting for the Object to be added to the container will be reduced to 0, and no matter how many threads are waiting for the Object to be released, the lock will not be released. When the execution of notify, notifyAll, interrupt or wait times out, the thread may be deleted from the waiting container. After obtaining the Object lock, the thread will re lock the Object (how many nests were there before, how many nests are there now, which is equivalent to recovery). After obtaining the CPU time slice, the thread will continue to execute. Incidentally, the number of locks held inside the Object (guess). Only when the number becomes 0 can other threads acquire the lock of the Object. (it's important to understand this concept!!! Otherwise, it's easy to mess with too many concepts)

notify cannot guarantee which thread is selected for deletion in the waiting capacity. notifyAll will delete all threads from the waiting capacity.

Interrupt will cause an InterruptedException exception after the object is locked again (when to lock again, as mentioned above). The interrupt status (isInterrupted) obtained in the try catch block is false. For more specific information, see the method description.

If a thread is notified and interrupted while waiting, it may be notified first and then interrupted, or it may be interrupted first and then notified.

Here you should know the state of the Thread. Readers can find that in fact, the Thread does not have a running state, but it has an executing state at the operating system level. Why? On the one hand, the CPU allocates a time slice to each Thread for scheduling. The time slice is generally tens of milliseconds. When one Thread executes a time slice, it will switch to the next Thread. Before switching, it will save the state of the current Thread, so that the state of the Thread can be loaded when switching back next time. Such a process is called context switching. Obviously, the switching between threads is very fast, At this time, it doesn't make much sense to distinguish between the running state and the ready state. You may see the running state in the last second, but it turns into the ready state before you react. Maybe you can only see the two states flashing each other; On the other hand, the Thread annotation says that the Thread in the runnable state is executing in the JVM, but it may be waiting for other resources from the operating system, such as processor, CPU and various hardware. The JVM regards these as resources. If something is serving the Thread, it thinks that the Thread is executing. For the operating system, its focus is the CPU. It must clarify the specific state of each Thread. Otherwise, for the runnable state, how does it know whether the Thread can be scheduled (some views are extracted from other people's articles). Finally, let's mention that the JVM sets the Thread state to prevent the Thread that has been started from being restarted.

data structure

    public class Thread implements Runnable {

        // Register local methods, such as start0 and stop0. As for what you have done, you can only say that it is too low-level. Those who do not understand C and C + + can be ignored
        private static native void registerNatives();
        static {
            registerNatives();
        }

        /**
         * The name of the thread
         * Why should the volatile keyword be added to the name of the thread?
         * Firstly, volatile ensures the visibility of memory, that is, when one thread modifies a shared variable, other threads can immediately see the modified value of the variable. More knowledge points will be described in another article
         * It is possible that multiple threads share this name variable
         */
        private volatile String name;

        /**
         * thread priority 
         * Note: different platforms may have different priorities, including 3 priorities and 10 priorities. Therefore, it is best to write thread when setting the priority MAX_ PRIORITY
         */
        private int priority;

        /**
         * Is this thread a daemon (background) thread
         */
        private boolean daemon = false;

        /**
         * The task to be executed by the thread, that is, the run method of the task will be called at last 
         */
        private Runnable target;

        /**
         * The thread group to which the thread belongs. Generally, if the thread does not specify a thread group, the thread group of the main thread is adopted by default, and where the main thread comes from, which depends on the startup of the JVM
         */
        private ThreadGroup group;

        /**
         * Class loader
         */
        private ClassLoader contextClassLoader;

        /**
         * To operate or decide on access control
         * 1. Decide whether to allow or deny access to critical system resources 
         * 2. Privileged code that affects access control decisions
         * 3. You can obtain the current context and make access control decisions from different contexts for the saved context
         */
        private AccessControlContext inheritedAccessControlContext;

        /**
         * When the thread does not specify a thread name, it will help us generate a new name - thread number, and the number will increase with the increase of threads, such as Thread-1 and Thread-2
         */
        private static int threadInitNumber;

        /**
         * Each thread has its own local stack - ThreadLocal, which maintains variables through ThreadLocalMap
         * ThreadLocalMap The bottom layer maintains an array, which maintains the relationship between key value pairs. The key is the current ThreadLocal object, that is, a ThreadLocal can only bind one value. If you want to bind multiple values, you need to define multiple ThreadLocal objects
         */
        ThreadLocal.ThreadLocalMap threadLocals = null;

        /**
         * Set the ThreadLocal of the current thread to the created thread. I don't know where to use it?
         */
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

        /**
         * Set the stack size, which is used in the lower level code. This attribute depends on different platforms and has different behaviors
         */
        private long stackSize;

        /**
         * The interruptible object is mainly used for I/O blocking. After the thread calls interrupt, it needs to wake up the selector
         */
        private volatile Interruptible blocker;

        /**
         * The default processor that does not handle the exception, that is, the exception is not caught and is usually null, that is, the exception stack information is printed directly on the console
         * If the default processor is set, all threads will apply the default processor. If the custom processor is set at the same time, the specified thread will only apply the custom processor
         */
        private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

        /**
         * Exception unhandled custom processor. If the custom processor is not specified, the default processor will be used
         */
        private volatile UncaughtExceptionHandler uncaughtExceptionHandler;

        /**
         * Record thread ID
         */
        private long tid;

        /**
         * In order to generate the thread ID, it shows an increasing trend. At first, the value is not 0, because the system thread will also be created inside the JVM
         */
        private static long threadSeqNumber;

        /**
         * Status of the thread
         * 0-NEW  1-RUNNABLE 4-RUNNABLE 2-TERMINATED 16-WAITING  32-TIMED-WAITING 1024-BLOCKED
         * Among them, 1 and 4 should be the so-called ready and running, but which corresponds to which is unknown. The thread state of the operating system is mapped to the thread state of the JVM
         */
        private volatile int threadStatus = 0;

        /**
         * Lowest priority
         */
        public final static int MIN_PRIORITY = 1;

        /**
         * normal priority 
         */
        public final static int NORM_PRIORITY = 5;

        /**
         * Maximum priority
         */
        public final static int MAX_PRIORITY = 10;
    }


Constructor

    /**
     * Initialize thread
     * Automatically generate the name of the thread. Format: Thread-i, I is an integer
     */
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * Initialize thread
     * The name of the auto build thread
     * @param target Assign task
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * Initialize thread
     * The name of the auto build thread
     * @param target Assign task
     * @param acc access control 
     */
    Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }

    /**
     * Initialize thread
     * The name of the auto build thread
     * @param group Thread group
     * @param target Assign task
     */
    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * Initialize the thread and specify the name of the thread
     * @param name The name of the thread
     */
    public Thread(String name) {
        init(null, null, name, 0);
    }

    /**
     * Initialize the thread and specify the name of the thread
     * @param group Thread group
     * @param name The name of the thread
     */
    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }

    /**
     * Initialize the thread and specify the name of the thread
     * @param target Assign task
     * @param name The name of the thread
     */
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }

    /**
     * Initialize the thread and specify the name of the thread
     * @param group Thread group
     * @param target Assign task
     * @param name The name of the thread
     */
    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }

    /**
     * Initialize the thread and specify the name of the thread
     * stackSize Depending on different platforms, there will be different values, so be careful when using this constructor
     * @param group Thread group
     * @param target Assign task
     * @param name The name of the thread
     * @param stackSize Stack size 
     */
    public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
        init(group, target, name, stackSize);
    }


Simple method

    /**
     * This method is mainly used in nio's selector. Here is an anonymous class that stores interruptable data
     * When a thread in I/O blocking calls interrupt, it needs to wake up the selector
     * The call of this method can be searched globally to know its purpose
     * @param b Interruptible object
     */
    void blockedOn(Interruptible b) {
        synchronized (blockerLock) {
            blocker = b;
        }
    }

    /**
     * Get thread name
     * @return Thread name
     */
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    /**
     * Get thread ID
     * @return Thread ID
     */
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

    /**
     * Gets the reference of the current thread
     */
    public static native Thread currentThread();

    /**
     * Each thread has a CPU time slice. This method refers to voluntarily giving up the CPU time slice so that other threads can execute faster
     */
    public static native void yield();

    /**
     * This method will not release the lock and can be used in asynchronous control methods or synchronous blocks
     * @param millis Specified sleep time in milliseconds
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * This method will not release the lock and can be used in asynchronous control methods or synchronous blocks
     * @param millis Specify the time in milliseconds
     * @param nanos Specify nanosecond time
     */
    public static void sleep(long millis, int nanos) throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) { //The unit conversion between nanosecond and millisecond is 6 zeros
            throw new IllegalArgumentException("nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }

    /**
     * Initialize thread
     * @param g Thread group
     * @param target Assign task
     * @param name Specifies the thread name
     * @param stackSize Stack size of thread
     */
    private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    /**
     * Initialize thread
     * @param g Thread group
     * @param target Assign task
     * @param name Specifies the thread name
     * @param stackSize Stack size of thread
     * @param acc Access control, such as whether to allow access to system resources
     * @param inheritThreadLocals Whether to set the inheritableThreadLocals of the current thread to the created thread, which is similar to inheritance
     */
    private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread(); //Get current thread
        SecurityManager security = System.getSecurityManager();
        if (g == null) {

            if (security != null) {
                g = security.getThreadGroup();
            }

            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        g.checkAccess(); //Determines whether the currently running thread has permission to modify this thread group

        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted(); //Record the number of threads that are not started, that is, the start method is not called

        this.group = g;
        this.daemon = parent.isDaemon(); //If the current thread of the created thread is a background (guard) thread, the created thread is also a background thread
        this.priority = parent.getPriority(); //The created thread will be the priority of the current thread
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

        this.stackSize = stackSize;

        tid = nextThreadID(); //Set thread ID
    }

    /**
     * Start the thread, and the underlying JVM calls the run method of the specified task
     * This method can only be called once
     */
    public synchronized void start() {
        
        if (threadStatus != 0) //threadStatus indicates that the current thread is in NEW status
            throw new IllegalThreadStateException();

        group.add(this); //Add the current thread to the thread group. At least what the thread group does may need to be explored in another article

        boolean started = false;
        try {
            start0(); //Start thread
            started = true;
        } finally {
            try {
                if (!started) { //If the startup fails, you need to remove it from the thread group
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                
            }
        }
    }

    /**
     * Start the thread. The bottom layer binds the C + + thread to the Java thread. What the C + + thread has done is unknown. I can guess it should be dealing with the operating system
     */
    private native void start0();

    /**
     * Perform tasks
     * If the thread does not call start and directly calls the run method, it is equivalent to executing a method, and there is no new thread
     */
    public void run() {
        if (target != null) {
            target.run();
        }
    }

    /**
     * This method is called by the JVM to make the thread clean up resources before exiting
     */
    private void exit() {
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        target = null;
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

    /**
     * Terminate the thread. Even the thread that has been started will be terminated immediately
     * 
     * 1. This method has been abandoned. Why is it abandoned? The official definition is as follows:
     * stop It is inherently unsafe Terminating a thread will cause the lock held by the thread to be released (the lock will be released when the ThreadDeath exception propagates upward to the stack)
     * If any object previously protected by these locks is in an inconsistent state, other threads may immediately view these inconsistent objects Such sub objects are considered damaged
     * When a thread operates on a damaged object, it will lead to unpredictable behavior, which may be difficult to detect Unlike non checked exceptions, ThreadDeath silently kills the thread (in short, we can't get any messages from ThreadDeath exceptions)
     * Therefore, the user does not warn that his program may be damaged, which is unpredictable
     * for instance:
     * synchronized void f() { 
     *       x = 1; 
     *       x = 2;
     *       x = 3;
     * }
     * It can run normally without calling the stop method, but after calling the stop method, the program is stopped immediately. At this time, we don't know where the program is executed, x = 1, x = 2 or x = 3
     * Therefore, even after other threads obtain the lock, they cannot determine the value of x. the first execution is 1, and the second execution may be 2, which leads to the unpredictable result, which is equivalent to the sudden destruction of the executing program at any time
     *
     * 
     * 2. Can't I catch the ThreadDeath exception and fix the damaged object?
     *    Threads can throw ThreadDeath exceptions almost anywhere. How do we know that the object is damaged? Then we have to slowly analyze each method that throws the exception
     *    When clearing from the first thread (in the catch or finally clause), the thread can throw the second ThreadDeath exception (finally throw ThreadDeath to ensure that the thread is terminated) Cleaning must be repeated until successful
     *
     * 3. What method is used instead of stop?
     *    Simply modify the value of the variable to indicate that the thread should be terminated. The thread should check the variable regularly. If the variable indicates that the operation is to be terminated, it should be returned from its run method in order
     *
     *    Mode 1:
     *    private volatile Thread blinker;
     * 
     *    public void stop() {
     *       blinker = null;
     *    }
     *
     *    public void run() {
     *      Thread thisThread = Thread.currentThread();
     *      while(blinker == thisThread){
     *         try {
     *              Thread.sleep(interval);
     *          } catch(InterruptedException e) {
     *              
     *          }
     *          repaint(); //Business logic
     *       }
     *    }
     *
     *    Mode 2:
     *    private final AtomicBoolean running = new AtomicBoolean(false);
     *    private int interval;
     *
     *    public ControlSubThread(int sleepInterval) {
     *       interval = sleepInterval;
     *    }
     * 
     * 
     *    public void stop() {
     *       running.set(false);
     *    }
     *
     *    public void run() { 
     *      running.set(true);
     *      while (running.get()) {
     *         try { 
     *             Thread.sleep(interval); 
     *         } catch (InterruptedException e){ 
     * 
     *         }
     *      }
     *    }
     *    
     */
    @Deprecated
    public final void stop() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            checkAccess();
            if (this != Thread.currentThread()) {
                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
            }
        }

        if (threadStatus != 0) {
            resume(); //Wake up pending threads
        }

        /**
         * You cannot catch the instance of ThreadDeath in the application. If you really want to catch, you must re throw the ThreadDeath exception object after processing the logic to determine that the thread is terminated
         * The exception handler can be customized in the thread, but the exception handler will not print any messages when targeting ThreadDeath
         */
        stop0(new ThreadDeath()); 
    }

    /**
     * Interrupt thread
     * 1. If the thread is in a waiting state when calling wait, join and sleep methods, you can call this method to interrupt the thread, that is, return from these methods and throw InterruptedException exceptions, and clear the interrupt state at the same time
     * 2. If calling the I/O operation causes the thread to be in the blocking state, you can call this method to close the channel, throw an exception and set the interrupt state
     * 3. If yes, call selector Select causes the thread to be in a blocking state. You can call this method to return the select immediately and set the interrupt state
     * 4. If none of the above conditions is true, the interrupt state of the thread will be set
     * Interrupting an unstarted thread has no effect
     */
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker; //We mentioned the function of blocker above, which is mainly used for selector in nio
            if (b != null) {
                interrupt0();
                b.interrupt(this);
                return;
            }
        }
        interrupt0();// 1,2,4
    }

    /**
     * Interrupt thread
     */
    private native void interrupt0();

    /**
     * If the current thread is interrupted, this method will clear the interrupt state
     * If the method is called multiple times, false will be returned after multiple calls, except for the interruption after multiple calls
     * @return Is it interrupted
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    /**
     * Whether the thread is interrupted or not, this method will not clear the interrupt state
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    /**
     * Whether the thread is interrupted and whether to clear the interrupt state depends on the ClearInterrupted parameter
     * @param ClearInterrupted Clear interrupt status
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

    /**
     * This method was originally designed to destroy the thread without cleaning up resources, that is, the lock held by the thread will continue to be held without releasing the lock. Fortunately, it has been abandoned before it has been implemented
     * Officials say that if it is implemented, it will lead to deadlock as easily as the Thread#suspend method. If the lock is not released, other threads cannot access it
     */
    @Deprecated
    public void destroy() {
        throw new NoSuchMethodError();
    }

    /**
     * Whether the thread is in the active state. The active state indicates that the thread has started (called start) and has not ended
     */
    public final native boolean isAlive();

    /**
     * Suspend / pause thread
     * 
     * 1. This method has been abandoned. Why? The official definition is as follows:
     * suspend It is inherently deadlock prone When a thread is suspended, its task still holds a lock on key system resources, and other threads cannot access the resources
     * If another thread attempts to acquire the lock before calling resume, it will cause a deadlock (the thread does not release the lock)
     *
     * 
     * 2. What method is used instead of suspend?
     *
     *    private volatile boolean threadSuspended;
     *   
     *    public synchronized void mousePressed() {
     *        //Business logic
     *
     *        threadSuspended = !threadSuspended;
     *
     *         if (!threadSuspended) {
     *            notify();
     *         }
     *     }
     *
     *     public void run() {
     *
     *          wihle(true) {  //The mission has not been exited here
     *              try {
     *                  Thread.sleep(interval);
     *
     *                  if(threadSuspended) { //This method can avoid the cost caused by synchronized keywords when the thread is not suspended
     *                      synchronized(this) {
     *                          while(threadSuspended) {
     *                              wait();
     *                          }
     *                      }
     *                  }
     *              } catch(InterruptedException e) {
     *    
     *              }
     *          }
     *          //Business logic
     *    }
     *
     *
     * 3. stop Combined with suspend
     *
     *    public void run() {
     *      Thread thisThread = Thread.currentThread();
     *      while (blinker == thisThread) { //It will exit normally after calling stop
     *          try {
     *              Thread.sleep(interval);
     *
     *              synchronized(this) {
     *                 while (threadSuspended && blinker==thisThread)
     *                     wait();
     *             }
     *          } catch (InterruptedException e){
     *
     *         }
     *          //Business logic
     *      }
     *    }

    public synchronized void stop() {
        blinker = null;
        notify();
    }
     *
     */
    @Deprecated
    public final void suspend() {
        checkAccess();
        suspend0();
    }

    /**
     * Suspend thread
     */
    private native void suspend0();

    /**
     * Resume thread
     * This method is only used with suspend, but because suspend is easy to cause deadlock and is abandoned, resume is not used
     */
    @Deprecated
    public final void resume() {
        checkAccess();
        resume0();
    }

    /**
     * Set thread priority
     * 1 < newPriority < g.maxPriority <= 10
     * @param newPriority Assign priority
     */
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

    /**
     * Get priority
     * @return priority
     */
    public final int getPriority() {
        return priority;
    }

    /**
     * Set the name of the thread
     * Even if the thread has been started, you can still set the name of the thread
     * @param name Specifies the name of the thread
     */
    public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) { // threadStatus != 0 indicates that the thread has started
            setNativeName(name);
        }
    }

    /**
     * Gets the name of the thread
     * @return The name of the thread
     */
    public final String getName() {
        return name;
    }

    /**
     * Get thread group
     * @return Thread group
     */
    public final ThreadGroup getThreadGroup() {
        return group;
    }

    /**
     * Gets the number of active threads in the current thread group
     * Thread subgroups are also included in thread calculation
     * This result is only an estimate, because the number of threads is always changing dynamically, and the JVM also has some threads, so it may affect the count result
     * This method is mainly used for debugging and monitoring
     * @return Number of active threads in thread group
     */
    public static int activeCount() {
        return currentThread().getThreadGroup().activeCount();
    }

    /**
     * Copies the active threads of the current thread group to the specified array
     * Strictly ensure that the size of the specified array is greater than the number of active threads
     * @param tarray Specify array
     * @return Number of active threads in thread group
     */
    public static int enumerate(Thread tarray[]) {
        return currentThread().getThreadGroup().enumerate(tarray);
    }

    /**
     * What is the stack frame of the computing thread? This involves the knowledge of JVM, and the author is still slowly moving forward
     * When calling this method, the thread should be suspended, and suspend has been abandoned, so this method is useless
     */
    @Deprecated
    public native int countStackFrames();

    /**
     * The current thread can wait up to the specified time
     * t1.join(time);
     * The current thread here is not t1, but the thread to which these belong, that is, the main thread. According to the top concept, the current thread should be added to the waiting container of t1 object
     *
     * @param millis Specify the time in milliseconds
     */
    public final synchronized void join(long millis) throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

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

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay); //If a thread in the waiting state calls interrupt to interrupt, it will directly throw an InterruptedException exception and resume operation. If notify is used, it will eventually wait for the specified millis time to resume operation
                now = System.currentTimeMillis() - base;
            }
        }
    }

    /**
     * The current thread can wait up to the specified time
     * If the specified time is set to 0, it will wait all the time
     * Did some parameter verification
     * @param millis Specify the time in milliseconds
     * @param nanos Specify the number of nanoseconds
     */
    public final synchronized void join(long millis, int nanos) throws InterruptedException {

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

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        join(millis);
    }

    /**
     * The current thread is waiting
     */
    public final void join() throws InterruptedException {
        join(0);
    }

    /**
     * Print thread stack to standard output
     * This method is only used for debugging
     */
    public static void dumpStack() {
        new Exception("Stack trace").printStackTrace();
    }

    /**
     * The value passed in determines whether to turn the thread into a background thread or a user thread
     * When all running threads are background threads, the JVM exits
     * This method must be called before start
     * false-User thread true - background thread
     * @param on sign
     */
    public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }

    /**
     * Is the thread a background (daemon) thread
     * @return Is it a background thread
     */
    public final boolean isDaemon() {
        return daemon;
    }

    /**
     * Whether the current thread has permission to modify the thread to which the current object belongs. If not, an exception will be thrown
     */
    public final void checkAccess() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkAccess(this);
        }
    }

    /**
     * Print thread name, priority, thread group
     * @return result
     */
    public String toString() {
        ThreadGroup group = getThreadGroup();
        if (group != null) {
            return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]";
        } else {
            return "Thread[" + getName() + "," + getPriority() + "," + "" + "]";
        }
    }

    /**
     * Whether the current thread holds the specified object lock
     * true-Indicates that the current thread holds the specified object lock
     * @return Whether the specified object lock is held
     */
    public static native boolean holdsLock(Object obj);

    /**
     * Gets the ID of the thread
     * @return ID of the thread
     */
    public long getId() {
        return tid;
    }

    /**
     * Gets the status of the current thread
     * @return Status of the current thread
     */
    public State getState() {
        return sun.misc.VM.toThreadState(threadStatus);
    }

    /**
     * Sets the default handler for unhandled exceptions
     * If the default processor is set, all threads will be applied to, which is equivalent to the global processor. If the custom processor is set at the same time, the specified thread will only apply the custom processor (see the dispatchUncaughtException method)
     * @param eh Default processor
     */
    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(
                new RuntimePermission("setDefaultUncaughtExceptionHandler")
                    );
        }
         defaultUncaughtExceptionHandler = eh;
     }

    /**
     * Gets the default handler for unhandled exceptions
     * Returning null indicates that there is no setting
     * @return Default processor
     */
    public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
        return defaultUncaughtExceptionHandler;
    }

    /**
     * Gets the custom handler for unhandled exceptions
     * If the custom processor is not set, the exception handling of the thread group will be followed, and the default processor will be obtained in the thread group. If it is not set, the stack information will be printed to the console. If it is set, the default processor will be followed
     * @return Custom processor
     */
    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group;
    }

    /**
     * Set custom processor
     * @param eh Custom processor
     */
    public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        checkAccess();
        uncaughtExceptionHandler = eh;
    }

    /**
     * Issue an exception to the specified processor, and this method will only be called by the JVM
     */
    private void dispatchUncaughtException(Throwable e) {
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }

    

summary

  • There are two ways to create threads. One is to inherit threads. The other is to implement Runnable. The second way is more common and can create multiple threads when there is only one task instance.

  • The six states of a thread - new, runnable, blocked, waiting, timeout waiting, and termination.

  • The implementation mechanism of wait is through the waiting container of the object. More specific description can refer to the concept.

  • We all know that each Thread has a local stack, which is actually maintained through the threadLocals variable in the Thread object, that is, even if the ThreadLocal variable is passed into different threads, it will not cause multiple threads to share the same ThreadLocal, because each Thread has a ThreadLocal object, that is, the threadLocals variable mentioned above. When setting the value for ThreadLocal, Each Thread will check whether the threadLocals variable under the current Thread is initialized. If not, initialize the variable. It should be noted that in ThreadLocal, the relationship between key value pairs is saved by maintaining a Map, and the key value is the current ThreadLocal object. In other words, passing ThreadLocal into different threads will cause the key value referenced by their threadLocals variable to be the same, but this has no impact.

  • sleep and yield do not release the object lock, and can be used in asynchronous control methods or synchronous blocks; wait, join, notify and notifyAll release object locks, which must be used in synchronization control methods or synchronization blocks. Because they operate on locks, they need to obtain locks before operation.

  • stop: because it will cause incomplete data and eventually lead to unpredictable behavior, it will be abandoned; Suspend: because the lock is not released all the time, it is easy to cause deadlock and discard; destroy: officials say it has been abandoned before it has been realized, which will also cause deadlock; resume: this method is only used with suspend. Since suspend has been abandoned, it has to be sacrificed.

  • Use of default and custom processors for unhandled exceptions.

a key

State transition of thread wait/join/notify/notify/sleep/yield

reference material

Java programming ideas

The art of Java Concurrent Programming

https://www.javazhiyin.com/52519.html

Tags: thread pool

Posted by clodius on Sat, 30 Apr 2022 12:13:21 +0300