java thread explanation

Thread state

In Java lang.Thread. Six threads are defined in the enumeration class state, as shown in the following figure

  • New: the thread state of a thread that has not been started.
  • Runnable: the thread state of the runnable thread, waiting for CPU scheduling (in two cases, running and waiting for CPU to execute).
  • Blocked: thread status that waits for the monitor to lock. Blocked in synchronized code block or method.
  • Waiting: the thread state of the waiting thread. The following methods without timeout: object wait,Thread.join,LockSupport.park
  • Timed Waiting: the thread state of the waiting thread with the specified waiting time. The following methods with timeout: thread sleep,Object.wait,Thread.join,LockSupport.parkNanos,LockSupport.parkUntil
  • Terminated: the thread state of the terminated thread. The thread completed execution normally or an exception occurred.

The transition between States is shown in the following figure:

Thread termination

In the Thread class, two methods are provided to terminate the Thread, namely stop() and interrupt().

  • Incorrect thread abort stop: stop aborts the thread and clears the information of the monitor lock, but it may cause thread safety problems. JDK is not recommended and has been marked @ Deprecated.
  • The correct thread aborts interrupt: if the target thread is blocked when calling the wait(), wait(long) or wait(long,int) methods, join(), join (long, int) or sleep (long, int) methods of Object class, interrupt will take effect, the interrupt state of the thread will be cleared, and the InterruptedException exception will be thrown; If the target thread is blocked by the Channel in I / O or NIO, the I / O operation will be interrupted or special exception values will be returned. Achieve the purpose of terminating threads; If none of the above conditions are met, the interrupt state of this thread will be set.
  • Correct thread abort flag bit: add a judgment in your own code logic to control the abort of thread execution.
public class Demo1 extends Thread {
    public volatile static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            try {
                while (flag) { // Determine whether to run
                    System.out.println("In operation");
                    Thread.sleep(1000L);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        // After 3 seconds, change the status flag to False, which means that the operation will not continue
        Thread.sleep(3000L);
        flag = false;
        System.out.println("End of program operation");
    }

}

Thread communication

In java, three API s for thread communication are provided: suspend/resume, wait/notify and park/unpark.

suspend/resume has been marked as abandoned. Why is it abandoned? It is because when a thread is suspended, if it obtains lock resources, it will not be released, which is easy to cause deadlock. And suspend must be executed before resume. If resume is executed first, it will also lead to deadlock.

/** Steamed stuffed bun shop */
	public static Object baozidian = null;

	/** Normal suspend/resume */
	public void suspendResumeTest() throws Exception {
		// Start thread
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // If there are no steamed stuffed buns, enter the waiting
				System.out.println("1,Enter waiting");
				Thread.currentThread().suspend();
			}
			System.out.println("2,Buy steamed stuffed buns and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce a steamed stuffed bun
		Thread.sleep(3000L);
		baozidian = new Object();
		consumerThread.resume();
		System.out.println("3,Inform consumers");
	}

	/** suspend/resume of deadlock. It's not as easy to write wait as it is to write wait */
	public void suspendResumeDeadLockTest() throws Exception {
		// Start thread
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // If there are no steamed stuffed buns, enter the waiting
				System.out.println("1,Enter waiting");
				// The current thread gets the lock and hangs
				synchronized (this) {
					Thread.currentThread().suspend();
				}
			}
			System.out.println("2,Buy steamed stuffed buns and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce a steamed stuffed bun
		Thread.sleep(3000L);
		baozidian = new Object();
		// After winning the lock, restore the consumerThread
		synchronized (this) {
			consumerThread.resume();
		}
		System.out.println("3,Inform consumers");
	}

	/** suspend/resume that causes the program to hang permanently */
	public void suspendResumeDeadLockTest2() throws Exception {
		// Start thread
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) {
				System.out.println("1,No steamed stuffed bun, enter and wait");
				try { // Add a little delay to this thread
					Thread.sleep(5000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// The pending execution here is after resume
				Thread.currentThread().suspend();
			}
			System.out.println("2,Buy steamed stuffed buns and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce a steamed stuffed bun
		Thread.sleep(3000L);
		baozidian = new Object();
		consumerThread.resume();
		System.out.println("3,Inform consumers");
		consumerThread.join();
	}

wait/notify mechanism: wait/notify these methods can only be called by the thread of the holder of the same object lock, that is, written in the synchronization block, otherwise an IllegalMonitorStateException will be thrown; Although the wait method can release lock resources, there are still requirements for the execution order. If the call of the wait method is started after notify is called, the thread will always be in the WAITING state.

  • Wait: the method causes the current thread to wait, add it to the waiting set of the object, and give up the currently held object lock.
  • The notify/notifyAll method wakes up one or all threads waiting for this object lock.
/** Normal wait/notify */
	public void waitNotifyTest() throws Exception {
		// Start thread
		new Thread(() -> {
			if (baozidian == null) { // If there are no steamed stuffed buns, enter the waiting
				synchronized (this) {
					try {
						System.out.println("1,Enter waiting");
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println("2,Buy steamed stuffed buns and go home");
		}).start();
		// After 3 seconds, produce a steamed stuffed bun
		Thread.sleep(3000L);
		baozidian = new Object();
		synchronized (this) {
			this.notifyAll();
			System.out.println("3,Inform consumers");
		}
	}

	/** wait/notify that will cause the program to wait permanently */
	public void waitNotifyDeadLockTest() throws Exception {
		// Start thread
		new Thread(() -> {
			if (baozidian == null) { // If there are no steamed stuffed buns, enter the waiting
				try {
					Thread.sleep(5000L);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				synchronized (this) {
					try {
						System.out.println("1,Enter waiting");
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println("2,Buy steamed stuffed buns and go home");
		}).start();
		// After 3 seconds, produce a steamed stuffed bun
		Thread.sleep(3000L);
		baozidian = new Object();
		synchronized (this) {
			this.notifyAll();
			System.out.println("3,Inform consumers");
		}
	}

park/unpark mechanism: when a thread calls Park, it waits for "permission", and the unpark method provides "permission" for the specified thread. The calling sequence of park and unpark methods is not required. Even if unpark is called first, the thread can run directly when the park method is called, and this effect will not be superimposed. For example, if you call the park method several times first, you will get the "permission" to run directly for the first time, and subsequent calls will enter the waiting mode. However, after Park, the lock resource will not be released, resulting in deadlock.

/** Normal park/unpark */
	public void parkUnparkTest() throws Exception {
		// Start thread
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // If there are no steamed stuffed buns, enter the waiting
				System.out.println("1,Enter waiting");
				LockSupport.park();
			}
			System.out.println("2,Buy steamed stuffed buns and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce a steamed stuffed bun
		Thread.sleep(3000L);
		baozidian = new Object();
		LockSupport.unpark(consumerThread);
		System.out.println("3,Inform consumers");
	}

	/** Deadlock park/unpark */
	public void parkUnparkDeadLockTest() throws Exception {
		// Start thread
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // If there are no steamed stuffed buns, enter the waiting
				System.out.println("1,Enter waiting");
				// The current thread gets the lock and hangs
				synchronized (this) {
					LockSupport.park();
				}
			}
			System.out.println("2,Buy steamed stuffed buns and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce a steamed stuffed bun
		Thread.sleep(3000L);
		baozidian = new Object();
		// After winning the lock, restore the consumerThread
		synchronized (this) {
			LockSupport.unpark(consumerThread);
		}
		System.out.println("3,Inform consumers");
	}

Warning! if statements should not be used in the code to determine whether to enter the waiting state, which is wrong! The official suggestion is that the waiting condition should be checked in the loop, because the thread in the waiting state may receive error alarm and false wake-up. if the waiting condition is not checked in the loop, the program will exit without meeting the end condition.
Pseudo wakeup: it means that the thread wakes up not because of api calls such as notify, notifyall and unpark, but because of lower level reasons.

Thread closure

Because when multithreading accesses shared variable data, it involves the problem of data synchronization between threads. However, shared data is not always used, so the concept of thread closure is put forward; Data is enclosed in their respective threads, so there is no need for synchronization. This technology of avoiding synchronization by enclosing data in threads is called thread closure. Thread closure is embodied in ThreadLocal and local variables.

Threadloadcl: ThreadLocal is a special variable in java. It is a thread level variable. Each thread has a ThreadLocal, which means that each thread has its own independent variable. The competitive conditions have been completely eliminated. It is an absolutely safe variable in the concurrent mode.

  • Usage: ThreadLocal var = new ThreadLocal(); A copy of T will be automatically created on each thread, and the copies are independent of each other. ThreadLocal can be used to store some parameters for use in multiple methods in a thread instead of method parameter passing.
  • Understanding: ThreadLocal can be understood as a Map < thread, t > maintained by the JVM. When each thread wants to use this T, it uses the current thread to get it from the Map. Only as an understanding concept.

Stack closure: one of the inherent properties of local variables is to be closed in the thread. They are located in the stack of the executing thread, which cannot be accessed by other threads.

Keywords: Java

Added by spider0176 on Tue, 08 Mar 2022 17:24:55 +0200