天天看點

Thread源碼閱讀

參考:Thread.join的作用和原理

參考:Thread類中的join()方法原理

文章目錄

    • 一.start方法和run方法差別
      • 1.先看總結
      • 2.源碼分析
    • 二.join:線程串行
    • 三.interrupt:告知停止方法
      • 1.如何安全地停止線程?
        • 1.循環标記變量自定義一個共享的boolean類型變量,表示目前線程是否需要中斷。
        • 2.循環中斷狀态中斷辨別 由線程對象提供,無需自己定義。
    • 四.sleep和wait差別

一.start方法和run方法差別

1.先看總結

start() : 它的作用是啟動一個新線程,新線程會執行相應的run()方法。start()不能被重複調用。

run() : run()就和普通的成員方法一樣,可以被重複調用。單獨調用run()的話,會在目前線程中執行run(),而并不會啟動新線程!

2.源碼分析

start方法:

public synchronized void start() {
        //1.如果這個線程已經被系統調用了就直接報錯,也就是說這個線程不能成功start兩次
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        //2.往線程組中添加這個線程
        group.add(this);

        boolean started = false;
        try {
            //3.調用native的啟動方法,真正的啟動線程
            start0();
            started = true;
        } finally {
            try {
                //4.線程啟動失敗要從group移除
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
           

run方法:

@Override
    public void run() {
        //1.直接啟動Runnable的繼承類的run方法,隻是方法調用,不是真正的線程調用
        if (target != null) {
            target.run();
        }
    }
           

二.join:線程串行

Thread類中的join方法的主要作用就是同步,它可以使得線程之間的并行執行變為串行執行

例如,A線程中調用了B線程的join方法,則相當于A線程調用了B線程的wait方法,在調用了B線程的wait方法後,A線程就會進入阻塞狀态

/**
     * 我們來着這樣的一段代碼.
     * Thread A(){
     *    @overrite
     *    run(){
     *        Thread b = new Thread();
     *        b.start();
     *        b.join();
     *    }
     * }
     * 這個方法就相當于在A線程中調用了B線程的wait方法,因為join就是會循環去執行wait.這樣的話就會讓A線程進入等待
     * 直到B線程執行結束之後才會調用notify方法去喚醒A線程
     * @param millis 等待時間
     * @throws InterruptedException
     */
    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");
        }
        //1.如果等待時間是0,就會循環一直等待
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            //2.如果目前線程存活,這邊計算wait的時間.
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
           

三.interrupt:告知停止方法

方法說明:

  • 将調用者線程的中斷狀态設為true。:public void interrupt()
  • 判斷調用者線程的中斷狀态。:public boolean isInterrupted()
  • 隻能通過Thread.interrupted()調用。 它會做兩步操作:傳回目前線程的中斷狀态;将目前線程的中斷狀态設為false:public static boolean interrupted

1.如何安全地停止線程?

stop函數停止線程過于暴力,它會立即停止線程,不給任何資源釋放的餘地,兩種安全停止線程的方法。

1.循環标記變量自定義一個共享的boolean類型變量,表示目前線程是否需要中斷。

中斷辨別:

volatile boolean interrupted = false;

任務執行函數:

Thread t1 = new Thread( new Runnable(){
    public void run(){
        while(!interrupted){
            // 正常任務代碼……
        }
        // 中斷處理代碼……
        // 可以在這裡進行資源的釋放等操作……
    }
} );
           

中斷函數:

Thread t2 = new Thread( new Runnable(){
    public void run(){
        interrupted = true;
    }
} );

           

2.循環中斷狀态中斷辨別 由線程對象提供,無需自己定義。

任務執行函數:

Thread t1 = new Thread( new Runnable(){
    public void run(){
        while(!Thread.currentThread.isInterrupted()){
            // 正常任務代碼……
        }
        // 中斷處理代碼……
        // 可以在這裡進行資源的釋放等操作……
    }
} );
           

中斷函數:

t1.interrupt();

上述兩種方法本質一樣,都是通過循環檢視一個共享标記為來判斷線程是否需要中斷,他們的差別在于:第一種方法的辨別位是我們自己設定的,而第二種方法的辨別位是Java提供的。除此之外,他們的實作方法是一樣的。

四.sleep和wait差別

兩個都是native方法,直接寫總結

  • sleep方法是Thread類的靜态方法,wait()是Object超類的成員方法
  • sleep()方法導緻了程式暫停執行指定的時間,讓出cpu該其他線程,但是他的監控狀态依然保持者,當指定的時間到了又會自動恢複運作狀态。在調用sleep()方法的過程中,線程不會釋放對象鎖。

    調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,隻有針對此對象調用notify()方法後本線程才進入對象鎖定池準備

  • sleep方法需要抛異常,wait方法不需要

繼續閱讀