創建線程的三種方式-繼承Thread,實現Runnable以及實現Callable

一. 創建線程的三種方式

1. 實現 Runnable接口

public class RunnableExample implements Runnable {
    @Override
    public void run() {
        System.out.println("implement Runnable");
    }

    public static void main(String[] args) {
        RunnableExample runnableExample = new RunnableExample();
        new Thread(runnableExample).start();
        //lambda 簡寫
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + ": run");
        }, "runnable-example").start();
    }
}

2. 繼承 Thread類 Thread類實現類Runnable接口

public class ThreadExample extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ": extend Thread");
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadExample threadExample = new ThreadExample();
        Thread thread = new Thread(threadExample);
        thread.start();
   }
 }  

3.實現Callable 接口

public class CallableExample implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "oh, my god ";
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.執行 Callable 方式,需要 FutureTask 實現類的支持,用於接收運算結果。
        CallableExample callableExample = new CallableExample();
        FutureTask<String> result = new FutureTask<String>(callableExample);
        new Thread(result).start();
        String res = result.get();
        System.out.println(res);
    }
}

二. 源碼分析三種方式

1. Runnable接口

Runnable接口,其實就是提供一個run()方法,讓實現它的類實現這個run()方法。

@FunctionalInterface
public interface Runnable {
    /**
     * 當使用實現接口Runnable的對象創建線程時,啓動該線程將導致在該單獨執行的線程中調用對象的run方法。 
     * run方法的一般約定是,它可以採取任何操作
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

2. Callable接口

Callable泛型接口,其實就是提供一個有返回類型的call()方法,讓實現它的類實現這個call()方法。

/**
 * 返回結果並可能引發異常的任務。實現者定義了一個沒有參數的方法call。
 *
 * Callable接口與Runnable類似,因爲它們都是爲實例可能由另一個線程執行的類設計的。
 * 但是,Runnable不返回結果,也不能拋出檢查過的異常。
 * executor類包含實用程序方法,用於從其他常見表單轉換爲可調用類。
 */
@FunctionalInterface
public interface Callable<V> {
    /**
     * 計算結果,或在無法這樣做時拋出異常
     *
     * @return 計算結果
     * @throws Exception 無法這樣做時拋出異常
     */
    V call() throws Exception;
}

3. Thread類

Thread實現類Runnable接口,所以實現了run()方法。

public class Thread implements Runnable {
    /* 確保本地註冊(類構造器方法方法用於類初始化)是創建一個線程首要做的事情。
     * 註冊的都是一些本地方法。
     */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private volatile String name; //線程名稱,可更改且線程可見
    private int            priority;//線程優先級
    private Thread         threadQ;  //沒發現用到
    private long           eetop;   //沒發現用到

    /* 線程是否單步 */
    private boolean     single_step; 

    /* 是否是守護線程 */
    private boolean     daemon = false;

    /* JVM state */
    private boolean     stillborn = false;

    /* 從構造方法傳過來的Runnable */
    private Runnable target; 

    /* 此線程所屬的組別 */
    private ThreadGroup group;

    /* 此類型的類加載器 */
    private ClassLoader contextClassLoader;

    /* 此線程繼承的訪問控制上下文*/
    private AccessControlContext inheritedAccessControlContext;

    /* 用於自動編號的匿名線程 默認爲0 */
    private static int threadInitNumber;
    //得到下一個線程編號從0開始 
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    /* 與此線程相關的ThreadLocal值。此映射由ThreadLocal類維護。 */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * 與此線程相關的可繼承線程本地值。此映射由InheritableThreadLocal類維護。
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    /*
     * 此線程請求的堆棧大小,如果創建者未指定堆棧大小,則爲0。
     * 這取決於VM對這個數字做它喜歡做的任何事情;一些VM會忽略它。
     */
    private long stackSize;

    /*
     *  此變量表示:在本地線程終止後,JVM私有的一個狀態值   
     */
    private long nativeParkEventPointer;

    /*
     * Thread ID 爲1開始
     */
    private long tid;

    /* 用於生成線程ID*/
    private static long threadSeqNumber;

    /* 
     * 爲工具提供的線程狀態值,初始化值表示當前線程還未運行
     */
    //初始狀態
    private volatile int threadStatus = 0;

     //私有同步方法,獲取下一個線程id 從1開始
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }
    
      /**
      * 此變量爲用於調用java.util.concurrent.locks.LockSupport.park方法的參數.
      * 其值由方法(private) java.util.concurrent.locks.LockSupport.setBlocker進行設定.
      * 其值訪問由方法java.util.concurrent.locks.LockSupport.getBlocker進行獲取.
      */
    volatile Object parkBlocker;

    /**
      * 在可中斷I/O操作中,本線程中的此對象會被阻塞.
        如果此線程的中斷狀態位被設置,則應該調用此阻塞對象的中斷方法.
      */
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();

    /*設定block變量的值;通過java.nio代碼中的 sun.misc.SharedSecrets進行調用.
     */
    void blockedOn(Interruptible b) {
        synchronized (blockerLock) {
            blocker = b;
        }
    }

    /**
     * 一個線程可以擁有的最低優先級 1
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * 線程的默認優先級 5 
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * 一個線程可以擁有的最高優先級. 10
     */
    public final static int MAX_PRIORITY = 10;

    /**
     * 返回當前正在執行線程對象的引用,注意這是一個本地方法。
     *
     * @return  the currently executing thread.
     */
    public static native Thread currentThread();

    /**
     *  yield()讓當前正在運行的線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行的機會。
     * 因此,使用yield()的目的是讓具有相同優先級的線程之間能夠適當的輪換執行。
     * 但是,實際中無法保證yield()達到讓步的目的,因爲,讓步的線程可能被線程調度程序再次選中。
     */
    public static native void yield();

    /**
     * 此方法會引起當前執行線程sleep(臨時停止執行)指定毫秒數.
     * 此方法的調用不會引起當前線程放棄任何監聽器(monitor)的所有權(ownership).
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * 同上 如果nanos >= 50萬 millis+1 如果小於50萬 millis不變
     */
    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);
    }

    /**
     * 利用當前訪問控制上下文(AccessControlContext)來初始化一個線程.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    /**
     * 初始化一個線程
     *
     * @param g the Thread group
     * @param target the object whose run() method gets called
     * @param name the name of the new Thread
     * @param stackSize  值爲0表示此參數被忽略
     * @param acc 用於繼承的訪問控制上下文
     * @param inheritThreadLocals 如果值爲true,從構造線程繼承可繼承線程局部變量的初始值
     */
    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();
        SecurityManager security = System.getSecurityManager();
        //如果所屬線程組爲null
        if (g == null) {
            //檢測其是否爲一個應用
            //如果有安全管理,查詢安全管理需要做的工作
            if (security != null) {
                g = security.getThreadGroup();
            }

            //如果安全管理在線程所屬線程組的問題上沒有什麼強制的要求strong opinion of the matter
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

      //無論所屬線程組是否顯示傳入,都要進行檢查訪問.
        g.checkAccess();

        //檢查是否有required權限 enableContextClassLoaderOverride
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        //當前正在執行的線程是否爲守護線程.
        this.daemon = parent.isDaemon();
        //優先級
        this.priority = parent.getPriority();
        //證明創建當前子類實例能夠忽略安全限制:子類不能覆蓋安全敏感,非final類型的方法,
        //否則enableContextClassLoaderOverride這個運行時許可會被堅持.
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
      	//目標runnable target
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* 設置線程ID,默認從1開始(++0) */
        tid = nextThreadID();
    }

    /**
     * 線程不支持淺拷貝.取而代之的是構造一個新的線程.
     *
     * @throws  CloneNotSupportedException
     *          always
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /**
     * 分配一個新的線程對象.此構造器和Thread(ThreadGroup,Runnable,String) 構造器的效果一樣.
     * 
     */
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
  
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }
  
    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(String name) {
        init(null, null, name, 0);
    }

    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }

    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }

    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }
  
    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
    }

    /**
     *  此方法的調用會引起當前線程的執行;JVM會調用此線程的run()方法.
     * 結果就是兩個線程可以併發執行:當前線程(從調用的start方法返回)和另一個線程(它在執行run方法).
     * 一個線程可以被調用多次.
     * 尤其注意:一個線程執行完成後可能並不會再被重新執行. 
     */
    public synchronized void start() {
          /**
         * 此方法並不會被主要方法線程or由虛擬機創建的系統組線程所調用.
         * 任何向此方法添加的新功能方法在未來都會被添加到虛擬機中.
         * 0狀態值代表了NEW的狀態. 此處表示不能重複start,會報錯。
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* 通知線程組組此線程即將啓動,以便可以將其添加到組的線程列表中,並且可以減少組的未啓動計數。 */
        group.add(this);
        //默認還沒開啓
        boolean started = false;
        try {
        //調用本地start0方法,設置當先線程已啓動
            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();

    /**
     * 如果此線程有runable對象,則執行,否則什麼也不執行.
     *
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

    /**
     * 此方法由系統調用,用於在一個線程退出前做一些掃尾工作.
     */
    private void exit() {
        //如果線程組不爲空
        if (group != null) {
            // 從group中  移除線程
            group.threadTerminated(this);
            group = null;
        }
        /*
          這樣一來, 在線程消亡後, 就沒有任何對象會持有threadlocalMap 對象的引用了, 自然裏面存儲的對象就可以被JVM 回收了。
          */
        target = null;
        /* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

 /**
  * 強制線程退出.此時會創建一個新的對象ThreadDeath作爲異常.
  * 允許對一個還沒有start的線程執行此方法.如果當前線程已經start了,則此方法的調用會使其立即停止.
  * 客戶端不應該經常去捕獲ThreadDeath異常,除非有一些額外的清除工作要做(注意:在線程死亡前,ThreadDeath的異常在拋出
  * 時會引發try對應的finally代碼塊的執行).如果catch捕獲了ThreadDeath對象,必須重新拋出此異常以保證線程可以真正的死亡.
  * 最頂級的錯誤處理器會對其它未被捕獲類型的異常進行處理,但是如果未處理異常是線程死亡的實例,則不會打印消息或通知應用程序。
    *此方法已經廢棄不用了 
  */
    @Deprecated
    public final void stop() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            checkAccess();
            if (this != Thread.currentThread()) {
                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
            }
        }
        // A zero status value corresponds to "NEW", it can't change to
        // not-NEW because we hold the lock.
        if (threadStatus != 0) {
            resume(); //如果線程是suspended掛起的,還有個喚醒操作,有可能會發生死鎖
        }

        // 虛擬機能處理所有的線程
        stop0(new ThreadDeath());
    }

    /**
     *
     */
    @Deprecated
    public final synchronized void stop(Throwable obj) {
        throw new UnsupportedOperationException();
    }

    /**
     *  此方法功能:中斷當前線程.
         * 除非當前線程在中斷自己(這麼做是允許的),此線程的checkAccess()方法被調用且拋出異常SecurityException
         * 1.如果當前線程由於wait類型方法,join類型方法或者sleep類型的方法的調用被阻塞,則它的中斷狀態將被清除且會收到一個
         * 中斷異常InterruptedException
         * 2.如果此線程由於java.nio.channels.InterruptibleChannel類中的InterruptibleChannel的I/O操作而被阻塞,
         * 則此方法會導致通道被關閉,且線程的中斷狀態會被重置,同時線程會收到一個異常ClosedByInterruptException.
         * 3.如果此線程由於java.nio.channels.Selector而阻塞,則線程的中斷狀態會被重置,且它將立即從阻塞的selection操作返回,
         * 且返回值通常是一個非零值,這就和java.nio.channels.Selector#wakeup的wakeup()方法被調用一樣.
         * 4.如果前面的條件都不成立,那麼該線程的中斷狀態將被重置.。
         * 中斷一個處於非活着狀態的線程並不需要產生任何其它影響.
     */
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();// 只是爲了設定中斷標識位
                b.interrupt(this); //中斷當前線程
                return;
            }
        }
        interrupt0();//只是爲了設置中斷標識位
    }

    /**
     * 測試當前線程是否被中斷.
     * 線程的中斷狀態會被此方法清除.
     * 換句話說,如果此方法兩次調用都能成功,則第二次調用的返回結果爲false(除非在第一次調用完後和第二次調用前,當前線程被再次中斷)
     * 因爲在中斷方法被調用時線程並未處於alive狀態而忽略線程中斷的情況會由於此方法的調用而受到影響.
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    /**
      * 查看當前線程是否被中斷.
     * 此方法的調用不會影響當前線程的中斷狀態.
     * 因爲在中斷方法被調用時線程並未處於alive狀態而忽略線程中斷的情況會由於此方法的調用而受到影響.
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    /**
     * 測試一些線程是否被中斷.
     * 中斷狀態會被重置or並不依賴於之前的中斷清除的值.
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

    @Deprecated
    public void destroy() {
        throw new NoSuchMethodError();
    }

    //測試當前線程是否處於存活狀態.如果一個線程在死亡狀態前都是存活狀態.
    public final native boolean isAlive();

    //將一個線程掛起
    @Deprecated
    public final void suspend() {
        checkAccess();
        suspend0();
    }

    //恢復一個懸掛狀態的線程
    @Deprecated
    public final void resume() {
        checkAccess();
        resume0();
    }

    /**
     *  配置優先級
     */
    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);
        }
    }

    public final int getPriority() {
        return priority;
    }

    public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }


    public final String getName() {
        return name;
    }
    
    public final ThreadGroup getThreadGroup() {
        return group;
    }

    /**
     * //返回線程所屬組的線程數,這是一個估計值.
     */
    public static int activeCount() {
        return currentThread().getThreadGroup().activeCount();
    }

   /**
     * 將線程所屬組和其子組中所有活着的線程拷貝到參數數組中.
     * 此方法只調用了一個方法java.lang.ThreadGroup.enumerate().
     * 一個應用如果想獲得這個線程數組,則它必須調用此方法,然而如果此數組太小而無法存放所有的線程,則放不下的線程
     * 就自動被忽略了.其實從線程組裏面獲取存活線程的方法是受到爭議的,此方法調用者應該證明方法返回值應該嚴格小於
     * 參數數組的長度.
     * 因爲此方法在被調用時存在競爭,因此建議此方法只用於debug和監聽目的.
     */
    public static int enumerate(Thread tarray[]) {
        return currentThread().getThreadGroup().enumerate(tarray);
    }

    /**
     * Counts the number of stack frames in this thread. The thread must
     * be suspended.
     *
     * @return     the number of stack frames in this thread.
     * @exception  IllegalThreadStateException  if this thread is not
     *             suspended.
     * @deprecated The definition of this call depends on {@link #suspend},
     *             which is deprecated.  Further, the results of this call
     *             were never well-defined.
     */
    @Deprecated
    public native int countStackFrames();

    /**
     * 最多等待參數millis(ms)時長當前線程就會死亡.參數爲0時則要持續等待.
       * 此方法在實現上:循環調用以this.isAlive()方法爲條件的wait()方法.
       * 當線程終止時notifyAll()方法會被調用.
       * 建議應用程序不要在線程實例上使用wait,notify,notifyAll方法.
     */
    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導致當前線程等待,直到另一個線程調用此對象的notify()方法或notifyAll()方法,或者等待指定的時間。
              	//jvm 在調用join,線程執行完的時候,會自動調用notitfy_all
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay); //Object的方法
                now = System.currentTimeMillis() - base;
            }
        }
    }

    /**
     * 
     */
    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);
    }

    //等待一直到線程死亡
    public final void join() throws InterruptedException {
        join(0);
    }

    /**
     * Prints a stack trace of the current thread to the standard error stream.
     * This method is used only for debugging.
     *
     * @see     Throwable#printStackTrace()
     */
    public static void dumpStack() {
        new Exception("Stack trace").printStackTrace();
    }

    /**
     * 設置是否爲守護線程
     */
    public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }

    /**
     * Tests if this thread is a daemon thread.
     *
     * @return  <code>true</code> if this thread is a daemon thread;
     *          <code>false</code> otherwise.
     * @see     #setDaemon(boolean)
     */
    public final boolean isDaemon() {
        return daemon;
    }

     
    public final void checkAccess() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkAccess(this);
        }
    }

    public String toString() {
        ThreadGroup group = getThreadGroup();
        if (group != null) {
            return "Thread[" + getName() + "," + getPriority() + "," +
                           group.getName() + "]";
        } else {
            return "Thread[" + getName() + "," + getPriority() + "," +
                            "" + "]";
        }
    }

    /**
		 *  獲取當前線程的類加載器
     * @since 1.2
     */
    @CallerSensitive
    public ClassLoader getContextClassLoader() {
        if (contextClassLoader == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader.checkClassLoaderPermission(contextClassLoader,
                                                   Reflection.getCallerClass());
        }
        return contextClassLoader;
    }

    /**
 	   * 設置上下文類加載器
     */
    public void setContextClassLoader(ClassLoader cl) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
        }
        contextClassLoader = cl;
    }
} 

4. 重點關注方法

Thread.join()方法

 /**
     *  millis 等待線程死亡的時間最多爲millis毫秒。超時爲0意味着永遠等待。
     *  這個實現使用這個的循環。在此條件下等待呼叫,我還活着。當線程終止此時。
     * 調用notifyAll方法。建議應用程序不要在線程實例上使用wait、notify或notifyAll。     
     * @param  millis
     *         等待線程死亡的時間最多爲millis毫秒。超時爲0意味着永遠等待。
     *
     * @throws  IllegalArgumentException
     *          millis 爲負數 拋出異常
     *
     * @throws  InterruptedException
     *          如果有線程中斷了當前線程。拋出此異常時清除當前線程的中斷狀態
     */
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
		//millis 爲負數 拋出異常
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
		//millis爲0 無限等待
        if (millis == 0) {
        	//測試此線程是否處於活動狀態。如果一個線程已經啓動並且還沒有死,那麼它就是活的
            while (isAlive()) {
            //導致當前線程等待,直到另一個線程調用此對象的notify()方法或notifyAll()方法,或者等待指定的時間。
                wait(0);
            }
        } else {
           //wait millis 時間
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

wait() 沒有被喚醒,是怎麼執行下面的流程?cpp裏面

// 位於/hotspot/src/share/vm/runtime/thread.cpp中
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
    // ...

    // Notify waiters on thread object. This has to be done after exit() is called
    // on the thread (if the thread is the last thread in a daemon ThreadGroup the
    // group should have the destroyed bit set before waiters are notified).
    // 有一個賊不起眼的一行代碼,就是這行
    ensure_join(this);

    // ...
}

static void ensure_join(JavaThread* thread) {
    // We do not need to grap the Threads_lock, since we are operating on ourself.
    Handle threadObj(thread, thread->threadObj());
    assert(threadObj.not_null(), "java thread object must exist");
    ObjectLocker lock(threadObj, thread);
    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
    // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
    java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
    // Clear the native thread instance - this makes isAlive return false and allows the join()
    // to complete once we've done the notify_all below
    java_lang_Thread::set_thread(threadObj(), NULL);

    // thread就是當前線程,被喚醒
    lock.notify_all(thread);

    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
}

Thread.interrupt()方法

在Java中,停止一個線程的主要機制是中斷,中斷並不是強迫終止一個線程,它是一種協作機制,是給線程傳遞一個取消信號,但是由線程來決定如何以及何時退出。

Thread類定義瞭如下方法:

public boolean isInterrupted();//測試此線程是否已被中斷。此方法不影響線程的中斷狀態
public void interrupt();//中斷線程
public static boolean interrupted();//測試此線程是否已被中斷,並清空中斷標誌位
interrupt()對線程的影響與線程的狀態和在進行的IO操作有關,我們先考慮線程的狀態:

RUNNABLE:線程在運行或具備運行條件只是在等待操作系統調度

WAITING/TIMED_WAITING:線程在等待某個條件或超時

BLOCKED:線程在等待鎖,試圖進入同步塊

NEW / TERMINATED:線程還未啓動或已結束

/**
 * 線程中斷總結
 * Thread  用來驗證,線程處於WAITING/TIMED_WAITING 時,調用interrupt()拋出中斷異常,並且中斷標誌恢復爲false
 * Thread2 用來驗證:如果線程在RUNNABLE時,且沒有執行IO操作,interrupt()只是會設置線程的中斷標誌位,中斷標誌爲true
 * Thread3 和 Thread4 用來驗證:)線程處於BLOCKED狀態,interrupt()只是會設置線程的中斷標誌位,也就是說,interrupt()並不能使一個在等待鎖的線程真正"中斷"。
 * @author 
 * @date 2020/06/11
 */
public class InterruptedExample {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("enmmm");
                    //thread 線程處於wait狀態
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    System.out.println("---InterruptedException---");
                    //false
                    System.out.println(isInterrupted());
                    e.printStackTrace();
                }
                System.out.println("a");
            }
        };
        thread.start();
        //處於wait的狀態會interrupt()中斷異常
        thread.interrupt();
        

        Thread thread2 = new Thread() {
            int i = 0;

            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println("沒有中斷執行中" + ++i);
                }
                System.out.println(Thread.currentThread().isInterrupted());
                System.out.println("中斷了");
            }
        };
        thread2.start();
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName());

        } catch (InterruptedException e) {
            System.out.println("thread2 中斷異常");
            e.printStackTrace();
        }
        thread2.interrupt();


        Object o = new Object();

        Thread thread3 = new Thread() {
            @Override
            public void run() {
                try {
                    synchronized (o) {
                        System.out.println("enmmm");
                        //thread 線程處於wait狀態
                        Thread.sleep(3000);
                        System.out.println("enmm3000m");
                    }
                } catch (InterruptedException e) {
                    System.out.println("---InterruptedException---");
                    //false
                    System.out.println(isInterrupted());
                    e.printStackTrace();
                }
                System.out.println("a");
            }
        };


        //如果線程在等待鎖,對線程對象調用interrupt()只是會設置線程的中斷標誌位,線程依然會處於BLOCKED狀態,也就是說,interrupt()並不能使一個在等待鎖的線程真正"中斷"。
        //使用synchronized關鍵字獲取鎖的過程中不響應中斷請求,這是synchronized的侷限性。如果這對程序是一個問題,應該使用顯式鎖。
        Thread thread4 = new Thread() {
            @Override
            public void run() {
                synchronized (o) {

                    System.out.println("thread4");
                }
            }
        };

        thread3.start();
        try {
            Thread.sleep(1000);
            thread4.start();
            thread4.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

線程中斷總結

  • 線程處於WAITING/TIMED_WAITING 時,調用interrupt()拋出中斷異常,並且中斷標誌恢復爲false
  • 線程處於RUNNABLE時,且沒有執行IO操作,interrupt()只是會設置線程的中斷標誌位,中斷標誌爲true
  • 線程處於BLOCKED狀態,interrupt()只是會設置線程的中斷標誌位,也就是說,interrupt()並不能使一個在等待鎖的線程真正"中斷"。

5. 從源碼中,可以總結幾點平時沒有留意到的

  • 線程初始化的時候,默認名稱爲Thread-N(N從0開始),線程ID 默認從1開始。
  • 線程不能重複調用start()方法,因爲每次判斷線程的狀態是否爲NEW狀態,不爲NEW拋異常。
  • 線程使用join()方法,可以保證線程的順序執行完畢,裏面用到了wait()方法,等待喚醒條件。
  • 線程interrupted()方法 ,不一定能使線程真的中斷。

三. 線程的生命週期

1. 線程的幾種狀態

生命週期的六種狀態

2. 線程狀態之間的轉換

六種狀態的轉換

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章