1. 程式人生 > 其它 >Java多執行緒——Thread類

Java多執行緒——Thread類

Java多執行緒——Thread類

Java 中執行緒實現方式有兩種:

  • 繼承Thread類,並重寫run方法
  • 實現Runnable介面的run方法

Thread類

使用方法:繼承Thread類,並重寫run方法

public class Demo {
    public static class MyThread extends Thread {
        public void run() {
            System.out.println("my thread");
        }
    }

    public static void main(String[] args) {
        Thread mythread = new MyThread();
        // 呼叫start()方法後,該執行緒才算啟動
        mythread.start();
    }
}

Runable 介面

使用方法:實現Runnable介面的run方法

public class Demo1 {
    public static class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println("MyThread");
        }
    }

    public static void main(String[] args) {
        new Demo.MyThread().start();
        // Java 8 函數語言程式設計,可以省略MyThread類
        new Thread(() -> {
            System.out.println("Java 8 匿名內部類");
        }).start();
    }
}

Java執行緒狀態

Thread 類裡有一個列舉型別 State,定義了執行緒的幾種狀態,分別有:

  • NEW:表示當前執行緒尚未啟動,NEW狀態表示例項化一個執行緒之後,但沒有開始執行,即 Thread 例項還沒呼叫 start() 方法。
  • RUNNABLE:表示當前執行緒正在執行中,RUNNABLE 狀態包括了作業系統執行緒狀態中的 Running 和 Ready,處於此狀態的執行緒可能正在執行,也可能正在等待系統資源。
  • BLOCKED:表示當前執行緒處於阻塞狀態,處於 BLOCKED 狀態的執行緒正等待鎖的釋放以進入同步區。
  • WAITING:表示當前執行緒處於無限期等待狀態,處於這種狀態的執行緒不會被分配CPU執行時間,它們要等待顯示的被其它執行緒喚醒。
  • TIMED_WAITING:表示當前執行緒處於超時等待狀態,處於這種狀態的執行緒也不會被分配 CPU 執行時間,不過無需等待被其它執行緒顯示的喚醒,在一定時間之後它們會由系統自動的喚醒。
  • TERMINATED:表示當前執行緒處於終止狀態,已終止執行緒的執行緒狀態,執行緒已經結束執行,即 run() 方法執行完成。

Java8 中 state 列舉類程式碼:

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;
}

檢視執行緒當前狀態可以呼叫 getState() 方法,並進入:

/**
  * Returns the state of this thread.
  * This method is designed for use in monitoring of the system state,
  * not for synchronization control.
  *
  * @return this thread's state.
  * @since 1.5
*/
public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}

// sun.misc.VM 原始碼:
public static State toThreadState(int var0) {
    if ((var0 & 4) != 0) {
        return State.RUNNABLE;
    } else if ((var0 & 1024) != 0) {
        return State.BLOCKED;
    } else if ((var0 & 16) != 0) {
        return State.WAITING;
    } else if ((var0 & 32) != 0) {
        return State.TIMED_WAITING;
    } else if ((var0 & 2) != 0) {
        return State.TERMINATED;
    } else {
        return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
    }
}

NEW:

例項化一個執行緒之後,並且這個執行緒沒有開始執行,這個時候的狀態就是 NEW,尚未啟動指的是還沒呼叫 Thread 例項的 start() 方法。

關於start() 的兩個引申問題:

  1. 反覆呼叫同一個執行緒的start()方法是否可行?
  2. 假如一個執行緒執行完畢(此時處於TERMINATED狀態),再次呼叫這個執行緒的start()方法是否可行?

檢視 start() 方法程式碼:

/**
 * Causes this thread to begin execution; the Java Virtual Machine
 * calls the <code>run</code> method of this thread.
 * <p>
 * The result is that two threads are running concurrently: the
 * current thread (which returns from the call to the
 * <code>start</code> method) and the other thread (which executes its
 * <code>run</code> method).
 * <p>
 * It is never legal to start a thread more than once.
 * In particular, a thread may not be restarted once it has completed
 * execution.
 *
 * @exception  IllegalThreadStateException  if the thread was already
 *               started.
 * @see        #run()
 * @see        #stop()
 */
public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

private native void start0();

在 start() 內部,這裡有一個 threadStatus 的變數。如果它不等於0,呼叫 start() 是會直接丟擲異常的。在呼叫一次start()之後,threadStatus 的值會改變(threadStatus !=0),此時再次呼叫 start() 方法會丟擲 IllegalThreadStateException 異常。

因此,兩個問題的答案都是不可行。

RUNNABLE:

表示當前執行緒正在執行中。處於RUNNABLE狀態的執行緒在Java虛擬機器中執行,也有可能在等待其他系統資源(比如I/O)。

RUNNABLE 狀態也可以理解為存活著正在嘗試徵用 CPU 的執行緒(有可能這個瞬間並沒有佔用CPU,但是它可能正在傳送指令等待系統排程)。由於在真正的系統中,並不是開啟一個執行緒後,CPU就只為這一個執行緒服務,它必須使用許多排程演算法來達到某種平衡,不過這個時候執行緒依然處於RUNNABLE狀態。

BLOCKED:

BLOCKED 稱為阻塞狀態,原因通常是它在等待一個“鎖”,當嘗試進入一個 synchronized 語句塊/方法時,鎖已經被其它執行緒佔有,就會被阻塞,直到另一個執行緒走完臨界區或發生了相應鎖物件的 wait() 操作後,它才有機會去爭奪進入臨界區的權利。

處於 BLOCKED 狀態的執行緒,即使對其呼叫 thread.interrupt() 也無法改變其阻塞狀態,因為 interrupt() 方法只是設定執行緒的中斷狀態,即做一個標記,不能喚醒處於阻塞狀態的執行緒。

注意:

ReentrantLock.lock() 操作後進入的是 WAITING 狀態,其內部呼叫的是 LockSupport.park() 方法。

WAITING:

處於這種狀態的執行緒不會被分配CPU執行時間,它們要等待顯示的被其它執行緒喚醒。WAITING 狀態通常是指一個執行緒擁有物件鎖後進入到相應的程式碼區域後,呼叫相應的“鎖物件”的 wait() 方法操作後產生的一種結果。變相的實現還有 LockSupport.park()、Thread.join()等,它們也是在等待另一個事件的發生,也就是描述了等待的意思。

以下方法會讓執行緒陷入無限期等待狀態:

  • 沒有設定timeout引數的Object.wait()

  • 沒有設定timeout引數的Thread.join()

  • LockSupport.park()

注意:

LockSupport.park(Object blocker) 會掛起當前執行緒,引數blocker是用於設定當前執行緒的“volatile Object parkBlocker 成員變數”

parkBlocker 是用於記錄執行緒是被誰阻塞的,可以通過LockSupport.getBlocker()獲取到阻塞的物件,用於監控和分析執行緒用的。

“阻塞”與“等待”的區別:

  • “阻塞”狀態是等待著獲取到一個排他鎖,進入“阻塞”狀態都是被動的,離開“阻塞”狀態是因為其它執行緒釋放了鎖,不阻塞了;

  • “等待”狀態是在等待一段時間 或者 喚醒動作的發生,進入“等待”狀態是主動的

如主動呼叫 Object.wait(),如無法獲取到 ReentraantLock,主動呼叫 LockSupport.park(),如主執行緒主動呼叫 subThread.join(),讓主執行緒等待子執行緒執行完畢再執行。離開“等待”狀態是因為其它執行緒發生了喚醒動作或者到達了等待時間。

TIMED_WAITING:

處於這種狀態的執行緒也不會被分配CPU執行時間,不過無需等待被其它執行緒顯示的喚醒,在一定時間之後它們會由系統自動的喚醒。

以下方法會讓執行緒進入TIMED_WAITING限期等待狀態:

  • Thread.sleep()方法

  • 設定了timeout引數的Object.wait()方法

  • 設定了timeout引數的Thread.join()方法

  • LockSupport.parkNanos()方法

  • LockSupport.parkUntil()方法

TERMINATED:

已終止執行緒的執行緒狀態,執行緒已經結束執行。這個狀態僅僅是 Java 語言提供的一個狀態,在作業系統內部可能已經登出了相應的執行緒,或者將它複用給其他需要使用執行緒的請求,而在Java語言級別只是通過Java程式碼看到的執行緒狀態而已。

執行緒狀態的轉換

執行緒狀態轉換圖:

BLOCKED狀態與RUNNABLE狀態的轉換

處於BLOCKED狀態的執行緒是因為在等待鎖的釋放。假如這裡有兩個執行緒a和b,a執行緒提前獲得了鎖並且暫未釋放鎖,此時b就處於BLOCKED狀態。

同時,run() 方法的執行是需要時間的,並不是啟動 start() 方法後就會立即執行,檢視以下例子:

public class Demo2 {

    public void blockedTest() {
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        }, "a");
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        }, "b");

        a.start();
        b.start();
        System.out.println(a.getName() + ":" + a.getState()); // 輸出?
        System.out.println(b.getName() + ":" + b.getState()); // 輸出?
    }

    // 同步方法爭奪鎖
    private synchronized void testMethod() {
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Demo2 demo2 = new Demo2();
        demo2.blockedTest();
    }
}

輸出結果:此時 a 執行緒已得到鎖,進入 run() 方法,但還沒有執行 Thread.sleep(),由於 a 執行緒已得到鎖,b 執行緒等待鎖的釋放,進入 BLOCKED 狀態。

a:RUNNABLE
b:BLOCKED

Process finished with exit code 0

調換語句順序,發現結果發生變化:

public void blockedTest() throws InterruptedException {
    ... ...
    a.start();
    System.out.println(a.getName() + ":" + a.getState()); // 輸出?
    b.start();
    System.out.println(b.getName() + ":" + b.getState()); // 輸出?
}

輸出結果:原因是測試方法的main執行緒只保證了a,b兩個執行緒呼叫start()方法(轉化為RUNNABLE狀態),還沒等兩個執行緒真正開始爭奪鎖,就已經列印此時兩個執行緒的狀態(RUNNABLE)了。

a:RUNNABLE
b:RUNNABLE

Process finished with exit code 0

讓 main 執行緒停一段時間,就可以得到 BLOCKED 狀態的結果:

public void blockedTest() throws InterruptedException {
    ... ...
    a.start();
    Thread.sleep(200L);
    System.out.println(a.getName() + ":" + a.getState()); // 輸出?
    b.start();
    Thread.sleep(200L);
    System.out.println(b.getName() + ":" + b.getState()); // 輸出?
}

輸出結果:此時 a 執行緒已得到鎖,正在執行 run() 方法,進入 TIMED_WAITING 狀態,b 執行緒由於得不到鎖,進入 BLOCKED 狀態

a:TIMED_WAITING
b:BLOCKED

Process finished with exit code 0

WAITING狀態與RUNNABLE狀態的轉換

有3個方法可以使執行緒從RUNNABLE狀態轉為WAITING狀態。

Thread.join():

public void blockedTest() {
    ······
    a.start();
    a.join();
    b.start();
    System.out.println(a.getName() + ":" + a.getState()); // 輸出 TERMINATED
    System.out.println(b.getName() + ":" + b.getState());
}

輸出結果:呼叫join()方法不會釋放鎖,會一直等待當前執行緒執行完畢(轉換為TERMINATED狀態)。b執行緒的狀態,有可能列印RUNNABLE(尚未進入同步方法),也有可能列印TIMED_WAITING(進入了同步方法)。

a:TERMINATED
b:TIMED_WAITING

Process finished with exit code 0

需要注意的是,其他執行緒呼叫notify()方法只會喚醒單個等待鎖的執行緒,如有有多個執行緒都在等待這個鎖的話不一定會喚醒到之前呼叫wait()方法的執行緒。

Object.wait():導致執行緒進入等待狀態,直到它被其他執行緒通過notify()或者notifyAll喚醒。該方法只能在同步方法中呼叫。如果當前執行緒不是鎖的持有者,該方法丟擲一個IllegalMonitorStateException異常。

使用示例:

public class WaitNotifyTest {
	public static void main(String[] args) {
		Object lock = new Object();
		
		new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("執行緒A等待獲取lock鎖");
                synchronized (lock) {
                    try {
                        System.out.println("執行緒A獲取了lock鎖");
                        Thread.sleep(1000);
                        System.out.println("執行緒A將要執行lock.wait()方法進行等待");
                        lock.wait();
                        System.out.println("執行緒A等待結束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
		
		new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("執行緒B等待獲取lock鎖");
                synchronized (lock) {
                    System.out.println("執行緒B獲取了lock鎖");
                    try {
                    	Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("執行緒B將要執行lock.notify()方法進行通知");
                    lock.notify();
                }
            }
        }).start();
	}
}

輸出結果:

執行緒A等待獲取lock鎖
執行緒A獲取了lock鎖
執行緒B等待獲取lock鎖
執行緒A將要執行lock.wait()方法進行等待
執行緒B獲取了lock鎖
執行緒B將要執行lock.notify()方法進行通知
執行緒A等待結束

Process finished with exit code 0

LockSupport.park():LockSupport.park() 呼叫的是 Unsafe 中的 native 程式碼,與Object類的wait/notify機制相比,park 以 thread 為操作物件更符合阻塞執行緒的直觀定義,操作更精準,可以準確地喚醒某一個執行緒增加了靈活性。

//LockSupport中
public static void park() {
        UNSAFE.park(false, 0L);
    }

TIMED_WAITING狀態與RUNNABLE狀態轉換

TIMED_WAITING 與 WAITING 狀態類似,只是 TIMED_WAITING 狀態等待的時間是指定的。

Thread.sleep(long):

使當前執行緒睡眠指定時間。需要注意這裡的“睡眠”只是暫時使執行緒停止執行,並不會釋放鎖。時間到後,執行緒會重新進入RUNNABLE狀態。

Sleep 方法程式碼:

public static native void sleep(long millis) throws InterruptedException;

// 納秒級別控制
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) {
        throw new IllegalArgumentException(
            "nanosecond timeout value out of range");
    }

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

    sleep(millis);
}

Object.wait(long):

wait(long) 方法使執行緒進入 TIMED_WAITING 狀態。這裡的wait(long)方法與無參方法 wait() 相同的地方是,都可以通過其他執行緒呼叫 notify() 或 notifyAll() 方法來喚醒。

不同的地方是,有參方法 wait(long) 就算其他執行緒不來喚醒它,經過指定時間 long 之後它會自動喚醒,擁有去爭奪鎖的資格。

Thread.join(long):

join(long) 使當前執行緒執行指定時間,並且使執行緒進入 TIMED_WAITING 狀態。

join(long) 的使用:呼叫 a.join(1000L),因為是指定了具體 a 執行緒執行的時間的,並且執行時間是小於 a 執行緒 sleep 的時間,所以 a 執行緒狀態輸出 TIMED_WAITING,b 執行緒狀態仍然不固定(RUNNABLE 或 BLOCKED)。

public void blockedTest() {
    ······
    a.start();
    a.join(1000L);
    b.start();
    System.out.println(a.getName() + ":" + a.getState()); // 輸出 TIEMD_WAITING
    System.out.println(b.getName() + ":" + b.getState());
}

Java執行緒中斷

Thread類裡提供的關於執行緒中斷的幾個方法:

  • Thread.interrupt():中斷執行緒。這裡的中斷執行緒並不會立即停止執行緒,而是設定執行緒的中斷狀態為true(預設是flase);
  • Thread.interrupted():測試當前執行緒是否被中斷。執行緒的中斷狀態受這個方法的影響,意思是呼叫一次使執行緒中斷狀態設定為true,連續呼叫兩次會使得這個執行緒的中斷狀態重新轉為false;
  • Thread.isInterrupted():測試當前執行緒是否被中斷。與上面方法不同的是呼叫這個方法並不會影響執行緒的中斷狀態。

“VisualVM執行緒監控執行緒狀態”與“Java執行緒狀態”對應關係總結:

通過 dump thread stack,並與 VisualVM 監控資訊中的執行緒名稱對應,找到的 VisualVM 每種執行緒狀態的執行緒堆疊如下:

1、執行:RUNNABLE

"http-bio-8080-Acceptor-0" daemon prio=6 tid=0x000000000d7b4800 nid=0xa264 runnable [0x000000001197e000]
      java.lang.Thread.State: RUNNABLE
            at java.net.DualStackPlainSocketImpl.accept0(Native Method)
            at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:131)
            at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)
            at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
            - locked <0x00000000c2303850> (a java.net.SocksSocketImpl)
            at java.net.ServerSocket.implAccept(ServerSocket.java:530)
            at java.net.ServerSocket.accept(ServerSocket.java:498)
            at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:60)
            at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:220)
            at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:
        - None


2、休眠:sleeping

"Druid-ConnectionPool-Destory-293325558" daemon prio=6 tid=0x000000000d7ad000 nid=0x9c94 waiting on condition [0x000000000bf0f000]
      java.lang.Thread.State: TIMED_WAITING (sleeping)
           at java.lang.Thread.sleep(Native Method)
            at com.alibaba.druid.pool.DruidDataSource$DestroyConnectionThread.run(DruidDataSource.java:1685)

Locked ownable synchronizers:
        - None


3、等待:wait

"Finalizer" daemon prio=8 tid=0x0000000009349000 nid=0xa470 in Object.wait() [0x000000000a82f000]
      java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x00000000c22a0108> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)

      - locked <0x00000000c22a0108> (a java.lang.ref.ReferenceQueue.Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
            at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

   Locked ownable synchronizers:
        - None

 

"JMX server connection timeout 45" daemon prio=6 tid=0x000000000e846000 nid=0xab10 in Object.wait() [0x00000000137df000]
      java.lang.Thread.State: TIMED_WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
           - waiting on <0x00000000c55da3f0> (a [I)
            at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
            - locked <0x00000000c55da3f0> (a [I)
            at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
         - None


4、駐留:park

"http-bio-8080-exec-2" daemon prio=6 tid=0x000000000d7b8000 nid=0x9264 waiting on condition [0x000000000ee4e000]
      java.lang.Thread.State: WAITING (parking)
            at sun.misc.Unsafe.park(Native Method)
           - parking to wait for  <0x00000000c5629bc8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject)
            at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
            at java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
            at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
            at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
            at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
            at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
            at java.util.concurrent.ThreadPoolExecutor.Worker.run(ThreadPoolExecutor.java:615)
            at org.apache.tomcat.util.threads.TaskThread.WrappingRunnable.run(TaskThread.java:61)
            at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
        - None


"pool-9-thread-1" prio=6 tid=0x000000000d7b2000 nid=0xd5fc waiting on condition [0x000000001187e000]
       java.lang.Thread.State: TIMED_WAITING (parking)
             at sun.misc.Unsafe.park(Native Method)
             - parking to wait for  <0x00000000c563b9e0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject)
             at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
             at java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
             at java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1090)
             at java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:807)
             at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
             at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
             at java.util.concurrent.ThreadPoolExecutor.Worker.run(ThreadPoolExecutor.java:615)
             at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
       - None


5、監視:monitor

"Thread-1" prio=6 tid=0x000000000a8a1800 nid=0xfdb4 waiting for monitor entry [0x000000000b4de000]
      java.lang.Thread.State: BLOCKED (on object monitor)
            at com.Test2$T.run(Test2.java:58)
           - waiting to lock <0x00000000eab757e0> (a java.lang.Object)

Locked ownable synchronizers:
      - None


彙總如下圖所示: