JAVA多線程(1)線程基礎

如何建立線程

Java中,建立線程的話,通常有兩種方式java

  • 繼承Thread類
  • 實現Runnable接口

繼承Thread類

public static void main(String[] args) {
        System.out.println("主線程ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
public class MyThread extends Thread {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("name:" + name + " 子線程ID:" + Thread.currentThread().getId());
    }
}

結果算法

主線程ID:1
name:thread2 子線程ID:1
name:thread1 子線程ID:12

start():

先來看看Java API中對於該方法的介紹:使該線程開始執行;Java 虛擬機調用該線程的 run 方法。結果是兩個線程併發地運行;當前線程(從調用返回給 start 方法)和另外一個線程(執行其 run 方法)。屢次啓動一個線程是非法的。特別是當線程已經結束執行後,不能再從新啓動,用start方法來啓動線程,真正實現了多線程運行,這時無需等待run方法體中的代碼執行完畢而直接繼續執行後續的代碼。經過調用Thread類的 start()方法來啓動一個線程,這時此線程處於就緒(可運行)狀態,並無運行,一旦獲得cpu時間片,就開始執行run()方法,這裏的run()方法 稱爲線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程隨即終止。安全

run():

一樣先看看Java API中對該方法的介紹:若是該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;不然,該方法不執行任何操做並返回。 Thread 的子類應該重寫該方法。run()方法只是類的一個普通方法而已,若是直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑仍是隻有一條,仍是要順序執行,仍是要等待run方法體執行完畢後纔可繼續執行下面的代碼,這樣就沒有達到寫線程的目的。多線程

結論

根據以上信息能夠得出結論併發

thread1和thread2的線程ID不一樣,thread2和主線程ID相同,說明調用start方法方可啓動新的線程,而run方法只是thread類中的一個普通方法調用,仍是在主線程裏執行,並不會建立新的線程app

雖然thread1的start方法調用在thread2的run方法前面調用,可是先輸出的是thread2的run方法調用的相關信息,說明新線程建立的過程不會阻塞主線程的後續執行less

實現Runnable接口

public class Main {
    public static void main(String[] args) {
        System.out.println("主線程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}
public class MyRunnable implements Runnable {

    public MyRunnable() {

    }

    @Override
    public void run() {
        System.out.println("子線程ID:" + Thread.currentThread().getId());
    }
}

結果ide

主線程ID:1
子線程ID:12

經過實現Runnable接口,咱們定義了一個子任務,而後將子任務交給Thread去執行,這種方式須要將Runnable做爲Thread類的參數,而後經過Thread的start方法來建立一個新的線程來執行子任務,若是調用Runnable的run方法就不會建立線程,和普通方法沒有區別,實際上Thread類就是實現了Runnable接口。函數

public class Thread implements Runnable {

在Java中,兩種方式均可以用來建立線程去執行子任務,可是Java只容許單繼承,因此若是自定義類須要繼承其餘類,就只能實現Runnable接口。oop

Thread類的使用

首先咱們來介紹下線程的狀態

線程狀態從大的方面來講,可歸結爲:初始/新建狀態(new)、可運行/就緒狀態(runnable)、運行狀態(running)、阻塞狀態(blocked、time waiting、waiting)、消亡狀態(dead)五個狀態:

新建狀態(New):

​ 當用new操做符建立一個線程時, 例如new Thread(r),線程尚未開始運行,此時線程處在新建狀態。 當一個線程處於新生狀態時,程序尚未開始運行線程中的代碼

就緒狀態(Runnable):

一個新建立的線程並不自動開始運行,要執行線程,必須調用線程的start()方法。當線程對象調用start()方法即啓動了線程,start()方法建立線程運行的系統資源,並調度線程運行run()方法。當start()方法返回後,線程就處於就緒狀態。
處於就緒狀態的線程並不必定當即運行run()方法,線程還必須同其餘線程競爭CPU時間,只有得到CPU時間才能夠運行線程。由於在單CPU的計算機系統中,不可能同時運行多個線程,一個時刻僅有一個線程處於運行狀態。所以此時可能有多個線程處於就緒狀態。對多個處於就緒狀態的線程是由Java運行時系統的線程調度程序(thread scheduler)來調度的。

運行狀態(Running)

當線程得到*CPU*時間後,它才進入運行狀態,真正開始執行run()方法.

阻塞狀態(Blocked):

線程運行過程當中,可能因爲各類緣由進入阻塞狀態:

  • 線程經過調用sleep方法進入睡眠狀態;
  • 線程調用一個在I/O上被阻塞的操做,即該操做在輸入輸出操做完成以前不會返回到它的調用者;
  • 線程試圖獲得一個鎖,而該鎖正被其餘線程持有;
  • 線程在等待某個觸發條件;
    所謂阻塞狀態是正在運行的線程沒有運行結束,暫時讓出CPU,這時其餘處於就緒狀態的線程就能夠得到CPU時間,進入運行狀態。

死亡狀態(Dead):

有兩個緣由會致使線程死亡:
1) run方法正常退出而天然死亡,
2) 一個未捕獲的異常終止了run方法而使線程猝死。
爲了肯定線程在當前是否存活着(就是要麼是可運行的,要麼是被阻塞了),須要使用isAlive方法。若是是可運行或被阻塞,這個方法返回true; 若是線程仍舊是new狀態且不是可運行的, 或者線程死亡了,則返回false.

img

上下文切換

線程上下文是指某一時間點 CPU 寄存器和程序計數器的內容,CPU經過時間片分配算法來循環執行任務(線程),由於時間片很是短,因此CPU經過不停地切換線程執行。

CPU在一個時刻只能運行一個線程,當在運行一個線程的過程當中轉去運行另一個線程,這個叫作線程上下文切換,這樣就須要記錄不一樣線程的運行狀態,以便下次從新切換回來時可以繼續切換到以前的狀態運行,上下文的切換實際上就是存儲和恢復CPU狀態的過程,使CPU能夠從端點繼續執行。

多線程可使任務執行效率得以提高,可是線程切換同樣有代價,開發中要權衡事後再設計方案,例如Redis就爲了快速而使用單線程

Thread類的方法

start()

start()用來啓動一個線程,當調用start方法後,系統纔會開啓一個新的線程來執行用戶定義的子任務,在這個過程當中,會爲相應的線程分配須要的資源。

/* The group of this thread 線程組        ThreadGroup是Java提供的一種對線程進行分組管理的手段,能夠對全部線程以組爲單位進行操做,爲線程服務,用戶經過使用線程組的概念批量管理線程,如批量中止或掛起、設置優先級、守護線程等
    */
    private ThreadGroup group;
    
    /**
     * 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        一個線程使用start方法、另外一個使用run方法,的結果是兩個線程同時運行
     * 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    線程已經存在異常
     * synchronized方法,保證在同一時刻,只有一個線程能夠執行某個方法或某個代碼塊,同時synchronized能夠保證一個線程的變化可見(可見性)
     *               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".
         * 若是線程狀態不爲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 */
            }
        }
    }
    /**
     * native關鍵字說明其修飾的方法是一個原生態方法,方法對應的實現不是在當前文件,而是在用其餘語言(如C和C++)實現的文件中。Java語言自己不能對操做系統底層進行訪問和操做,可是能夠經過JNI接口調用其餘語言來實現對底層的訪問
     * 這個關鍵字表示調用本機的操做系統函數,由於多線程須要底層操做系統的支持
     */
    private native void start0();

run()

run()方法是不須要用戶來調用的,當經過start方法啓動一個線程以後,當線程得到了CPU執行時間,便進入run方法體去執行具體的任務。注意,繼承Thread類必須重寫run方法,在run方法中定義具體要執行的任務。

/**
 * If this thread was constructed using a separate    若是這個線程使用單獨的構造Runnable運行對象,那麼會調用Runnable對象的run方法,不然什麼都不會返回
 * <code>Runnable</code> run object, then that
 * <code>Runnable</code> object's <code>run</code> method is called;
 * otherwise, this method does nothing and returns.
 * <p>
 * Subclasses of <code>Thread</code> should override this method.    使用Thread應該重寫此方法
 *
 * @see     #start()
 * @see     #stop()
 * @see     #Thread(ThreadGroup, Runnable, String)
 */
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

sleep()

sleep方法有兩個重載版本:

/**
     * Causes the currently executing thread to sleep (temporarily cease    致使當前正在執行的線程進入睡眠狀態(暫時中止執行)以指定的毫秒數爲準,基於系統計時器和調度程序的精度和準確性,不會失去監視器全部權
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds plus the specified
     * number of nanoseconds, subject to the precision and accuracy of system
     * timers and schedulers. The thread does not lose ownership of any
     * monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @param  nanos    額外的納秒睡眠
     *         {@code 0-999999} additional nanoseconds to sleep
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative, or the value of
     *          {@code nanos} is not in the range {@code 0-999999}
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    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);
    }

sleep方法不會釋放鎖,也就是說若是當前線程持有對某個對象的鎖,則即便調用sleep方法,其餘線程也沒法訪問這個對象

public class Main {
    private int i = 10;
    private Object object = new Object();

    public static void main(String[] args) {
        Main main = new Main();
        MyThread thread1 = main.new MyThread();
        MyThread thread2 = main.new MyThread();
        thread1.start();
        thread2.start();
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                i++;
                System.out.println("i:"+i);
                try {
                    System.out.println("線程"+Thread.currentThread().getName()+"進入睡眠狀態");
                    Thread.currentThread().sleep(5000);
                } catch (InterruptedException e) {
                    // TODO: handle exception
                }
                System.out.println("線程"+Thread.currentThread().getName()+"睡眠結束");
                i++;
                System.out.println("i:"+i);
            }
        }
    }
}
i:11
線程Thread-0進入睡眠狀態
線程Thread-0睡眠結束
i:12
i:13
線程Thread-1進入睡眠狀態
線程Thread-1睡眠結束
i:14

  從上面輸出結果能夠看出,當Thread-0進入睡眠狀態以後,Thread-1並無去執行具體的任務。只有當Thread-0執行完以後,此時Thread-0釋放了對象鎖,Thread-1纔開始執行。

  注意,若是調用了sleep方法,必須捕獲InterruptedException異常或者將該異常向上層拋出。當線程睡眠時間滿後,不必定會當即獲得執行,由於此時可能CPU正在執行其餘的任務。因此說調用sleep方法至關於讓線程進入阻塞狀態

語法方面:
sleep方法的簽名裏面有InterruptedException 這個checked exception,因此調用方必須捕獲或者再次拋出。

線程的中斷合做機制方面:
對於java的多線程來講,調用一個能夠被阻塞的方法(wait,sleep,join等等),意味着代碼到這行會被阻塞,那麼中斷阻塞的時候應該怎麼辦?
通常來講,是採用InterruptedException配合Interrupt狀態來合做完成的。
若是你調用的方法是個會阻塞的方法(通常會拋出InterruptedException),通用的作法是:
1,若是你的方法簽名能夠包含InterruptedException,那麼直接拋出,讓你的方法也變成一個阻塞方法。
2,若是你的方法簽名不容許你增長InterruptedException,那麼須要你在捕獲InterruptedException後及時重置Interrupt狀態(通常你調用的阻塞函數在拋出InterruptedException後會清除Interrupt狀態)。
以上2種手法的目的都是爲了上層調用方能知道本線程被中斷了,而後一直將這個Interrupt狀態傳遞到線程的管理者模塊那,由他再決定如何處理這個線程。

因此你不必定須要捕獲,根據你的方法的簽名,決定採用方案1或者2來處理這個異常。

yield()

調用yield方法會讓當前線程交出CPU權限,讓CPU去執行其餘的線程。它跟sleep方法相似,一樣不會釋放鎖。可是yield不能控制具體的交出CPU的時間,另外,yield方法只能讓擁有相同優先級的線程有獲取CPU執行時間的機會。注意,調用yield方法並不會讓線程進入阻塞狀態,而是讓線程重回就緒狀態,它只須要等待從新獲取CPU執行時間,這一點是和sleep方法不同的。

/**
     * A hint to the scheduler that the current thread is willing to yield    給調度程序的提示當前線程願意放棄對處理器的當前使用。 調度程序能夠隨意忽略此提示
     * its current use of a processor. The scheduler is free to ignore this 
     * hint.
     *
     * <p> Yield is a heuristic attempt to improve relative progression         Yield是一種啓發式嘗試,旨在提升線程之間的相對進程,不然將過分利用CPU。 應將其使用與詳細的性能分析和基準測試結合起來,以確保它實際上具備所需的效果。
     * between threads that would otherwise over-utilise a CPU. Its use
     * should be combined with detailed profiling and benchmarking to
     * ensure that it actually has the desired effect.
     *
     * <p> It is rarely appropriate to use this method. It may be useful    不多適合使用此方法。 它可能對調試或測試有用,由於它可能有助於重現因爲競爭條件而產生的錯誤。 當設計諸如{@link java.util.concurrent.locks}包中的併發
     * for debugging or testing purposes, where it may help to reproduce    控制結構時,它也可能頗有用。
     * bugs due to race conditions. It may also be useful when designing
     * concurrency control constructs such as the ones in the
     * {@link java.util.concurrent.locks} package.
     */
    public static native void yield();

join()

假如在main線程中,調用thread.join方法,則main方法會等待thread線程執行完畢或者等待必定的時間。若是調用的是無參join方法,則等待thread執行完畢,若是調用的是指定了時間參數的join方法,則等待必定的事件。

/**
     * Waits for this thread to die.    等待線程死亡
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join() throws InterruptedException {
        join(0);
    }    


    /**
     * Waits at most {@code millis} milliseconds for this thread to    等待線程死亡,設置等待時間上限
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls    此實現使用以{@code this.isAlive}爲條件的{@code this.wait}調用循環。 線程終止時,將調用{@code this.notifyAll}方法。 建議應用程序不要在{@code Thread}
     * conditioned on {@code this.isAlive}. As a thread terminates the    實例上使用{@code wait},{@ code notify}或{@code notifyAll}。
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    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()) {
                //線程須要存活狀態才能繼續等待,不然結束,等到已等待時間大於或等於millis,跳出循環
                long delay = millis - now;
                //若是等待時間結束
                if (delay <= 0) {
                    break;
                }
                //調用wait方法,讓線程進入阻塞狀態,並釋放線程佔有的鎖,交出CPU執行權限,sleep則不會釋放鎖
                wait(delay);
                //已等待時間
                now = System.currentTimeMillis() - base;
            }
        }
    }

    /**
     * Waits at most {@code millis} milliseconds plus
     * {@code nanos} nanoseconds for this thread to die.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @param  nanos
     *         {@code 0-999999} additional nanoseconds to wait
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative, or the value
     *          of {@code nanos} is not in the range {@code 0-999999}
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    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 class JoinMain {
    public static void main(String[] args) throws IOException {
        System.out.println("進入線程" + Thread.currentThread().getName());
        JoinMain joinMain = new JoinMain();
        MyThread thread1 = joinMain.new MyThread();
        thread1.start();
        try {
            System.out.println("線程" + Thread.currentThread().getName() + "等待");
            //等待線程死亡
            thread1.join();
            System.out.println("線程" + Thread.currentThread().getName() + "繼續執行");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("進入線程" + Thread.currentThread().getName());
            try {
                Thread.currentThread().sleep(5000);
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
            System.out.println("線程" + Thread.currentThread().getName() + "執行完畢");
        }
    }
}

根據打印內容,咱們能夠看到,線程thread1執行後,直到執行完畢纔開始繼續執行main線程,因爲有wait方法,全部join一樣也會讓線程釋放對一個對象持有的鎖。

進入線程main
線程main等待
進入線程Thread-0
線程Thread-0執行完畢
線程main繼續執行

interrupt()

interrupt,顧名思義,即中斷的意思。單獨調用interrupt方法可使得處於阻塞狀態的線程拋出一個異常,也就說,它能夠用來中斷一個正處於阻塞狀態的線程;另外,經過interrupt方法和isInterrupted()方法來中止正在運行的線程

/**
     * Interrupts this thread.
     *
     * <p> Unless the current thread is interrupting itself, which is    中斷此線程。<p>除非當前線程一直在中斷自身(老是容許這樣作),不然將調用此線程的{@link #checkAccess()checkAccess}方法,這可能會引起{@link 
     * always permitted, the {@link #checkAccess() checkAccess} method    SecurityException}。
     * of this thread is invoked, which may cause a {@link
     * SecurityException} to be thrown.
     *
     * <p> If this thread is blocked in an invocation of the {@link                <p>若是在調用{@link Object#wait()wait()},{@ link Object#wait(long)wait(long)}或{@link Object#wait(long) {@link Object}類或
     * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link    {@link #join()},{@ link #join(long)},{@ link #join(long, int)},{@ link #sleep(long)}或{@link #sleep(long,int)}這類方法,則
     * Object#wait(long, int) wait(long, int)} methods of the {@link Object}    其中斷狀態將被清除,而且將收到{@link InterruptedException}。
     * class, or of the {@link #join()}, {@link #join(long)}, {@link
     * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
     * methods of this class, then its interrupt status will be cleared and it
     * will receive an {@link InterruptedException}.
     *
     * <p> If this thread is blocked in an I/O operation upon an {@link            <p>若是此線程在{@link java.nio.channels.InterruptibleChannel InterruptibleChannel}上的I / O操做中被阻塞,則該通道將被關閉,該線程的中斷狀
     * java.nio.channels.InterruptibleChannel InterruptibleChannel}                態將被設置,而且該線程將收到一個{ @link java.nio.channels.ClosedByInterruptException}。
     * then the channel will be closed, the thread's interrupt
     * status will be set, and the thread will receive a {@link
     * java.nio.channels.ClosedByInterruptException}.
     *
     * <p> If this thread is blocked in a {@link java.nio.channels.Selector}    <p>若是此線程在{@link java.nio.channels.Selector}中被阻塞,則將設置該線程的中斷狀態,而且它將當即從選擇操做中返回,可能具備非零值,就像選擇器的
     * then the thread's interrupt status will be set and it will return        {@link java.nio.channels.Selector#wakeup喚醒}方法已被調用。
     * immediately from the selection operation, possibly with a non-zero
     * value, just as if the selector's {@link
     * java.nio.channels.Selector#wakeup wakeup} method were invoked.
     *
     * <p> If none of the previous conditions hold then this thread's interrupt        <p>若是沒有以上條件,則將設置該線程的中斷狀態。 </ p>
     * status will be set. </p>
     *
     * <p> Interrupting a thread that is not alive need not have any effect.        <p>中斷未運行的線程不會產生任何效果。
     *
     * @throws  SecurityException
     *          if the current thread cannot modify this thread                        若是當前線程沒法修改此線程,則@throws SecurityException
     *
     * @revised 6.0
     * @spec JSR-51
     */
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();
        
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                //native方法
                interrupt0();           // Just to set the interrupt flag
                //設置中斷標誌位
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

    /**
     * Tests whether the current thread has been interrupted.  The                測試當前線程是否已被中斷。 經過此方法能夠清除線程的<i>中斷狀態</ i>。 換句話說,若是要連續兩次調用此方法,則第二次調用將返回false(除非當前線程
     * <i>interrupted status</i> of the thread is cleared by this method.  In    在第一次調用清除其中斷狀態以後且在第二次調用檢查其狀態以前再次中斷)。 p>因爲當前中斷已被中斷<code> false </ @返回<code> true </ code>,該方法
     * other words, if this method were to be called twice in succession, the    將返回false,從而忽略因爲線程中斷而致使線程中斷的忽略。 代碼>不然。@ see #isInterrupted()@修訂6.0
     * second call would return false (unless the current thread were
     * interrupted again, after the first call had cleared its interrupted
     * status and before the second call had examined it).
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if the current thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see #isInterrupted()
     * @revised 6.0
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    /**
     * Tests whether this thread has been interrupted.  The <i>interrupted    測試此線程是否已被中斷。 該方法不影響線程的<i>中斷狀態</ i>。<p>因爲該方法返回false將反映線程中斷,由於該線程在中斷時未處於活動狀態。@ return < 
     * status</i> of the thread is unaffected by this method.    code> true </ code>,若是此線程已被中斷<code> false </ code>,不然。@ see #interrupted()@revised 6.0
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if this thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see     #interrupted()
     * @revised 6.0
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }
public class InterruptMain {
    public static void main(String[] args) throws IOException {
        InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread {
        @Override
        public void run() {
            try {
                System.out.println("進入睡眠狀態");
                Thread.currentThread().sleep(10000);
                System.out.println("睡眠完畢");
            } catch (InterruptedException e) {
                System.out.println("獲得中斷異常");
            }
            System.out.println("run方法執行完畢");
        }
    }
}

從這裏能夠看出,經過interrupt方法能夠中斷處於阻塞狀態的線程。

進入睡眠狀態
獲得中斷異常
run方法執行完畢

接下來看一下經過interrupt方法是否能夠中斷非阻塞狀態的線程。

public class InterruptMain {
    public static void main(String[] args) throws IOException {
        InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            int i = 0;
            while(i<Integer.MAX_VALUE){
                System.out.println(i+" while循環");
                i++;
            }
        }
    }
}

從結果看出,while循環會一直運行直到變量i的值超出Integer.MAX_VALUE。因此說直接調用interrupt方法不能中斷正在運行中的線程。

9766171 while循環
9766172 while循環
9766173 while循環
9766174 while循環
9766175 while循環

雖然直接調用interrupt方法不能中斷正在運行中的線程,可是可使用isInterrupted()判斷中斷標誌是否被置位來中斷線程的執行。

public class InterruptMain {
    public static void main(String[] args) throws IOException {
        InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            //Thread.currentThread().sleep(2000);
            Thread.sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread {
        @Override
        public void run() {
            int i = 0;
            while (!isInterrupted() && i < Integer.MAX_VALUE) {
                System.out.println(i + " while循環");
                i++;
            }
        }
    }
}

從結果看出,打印若干個值以後,while循環就中止打印了。

可是通常狀況下不建議經過這種方式來中斷線程,通常會在MyThread類中增長一個屬性 isStop來標誌是否結束while循環,而後再在while循環中判斷isStop的值

416317 while循環
416318 while循環
416319 while循環
416320 while循環
416321 while循環
416322 while循環

這樣就能夠在外面經過調用setStop方法來終止while循環

class MyThread extends Thread{
        private volatile boolean isStop = false;
        @Override
        public void run() {
            int i = 0;
            while(!isStop){
                i++;
            }
        }
         
        public void setStop(boolean stop){
            this.isStop = stop;
        }
    }

stop()

stop方法已是一個廢棄的方法,它是一個不安全的方法。由於調用stop方法會直接終止run方法的調用,而且會拋出一個ThreadDeath錯誤,若是線程持有某個對象鎖的話,會徹底釋放鎖,致使對象狀態不一致。因此stop方法基本是不會被用到的。

destroy()

已廢棄

關於線程屬性的方法

1)getId

  用來獲得線程ID

/**
     * Returns the identifier of this Thread.  The thread ID is a positive    返回此線程的標識符。 線程ID是建立該線程時生成的正<tt> long </ tt>號。
     * <tt>long</tt> number generated when this thread was created.            線程ID是惟一的,而且在其生命週期內保持不變。 當線程終止時,能夠從新使用該線程ID。
     * The thread ID is unique and remains unchanged during its lifetime.
     * When a thread is terminated, this thread ID may be reused.
     *
     * @return this thread's ID.
     * @since 1.5
     */
    public long getId() {
        return tid;
    }

2)getName和setName

  用來獲得或者設置線程名稱。

/**
     * Changes the name of this thread to be equal to the argument    將該線程的名稱更改成等於參數<code> name </ code>。<p>
     * <code>name</code>.                                
     * <p>
     * First the <code>checkAccess</code> method of this thread is called    首先,不帶任何參數調用此線程的<code> checkAccess </ code>方法。 這可能致使拋出<code> SecurityException </ code>。@ param name這個線程的新名稱。 
     * with no arguments. This may result in throwing a                       @exception SecurityException若是當前線程沒法修改此異常
     * <code>SecurityException</code>.
     *
     * @param      name   the new name for this thread.
     * @exception  SecurityException  if the current thread cannot modify this
     *               thread.
     * @see        #getName
     * @see        #checkAccess()
     */
    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);
        }
    }

    /**
     * Returns this thread's name.    返回線程的名稱
     *
     * @return  this thread's name.
     * @see     #setName(String)
     */
    public final String getName() {
        return name;
    }

3)getPriority和setPriority

  用來獲取和設置線程優先級。

/**
     * Changes the priority of this thread.                                更改此線程的優先級。<p>
     * <p>
     * First the <code>checkAccess</code> method of this thread is called    首先,不帶任何參數調用此線程的<code> checkAccess </ code>方法。 這可能致使拋出<code> SecurityException </ code>。
     * with no arguments. This may result in throwing a
     * <code>SecurityException</code>.
     * <p>
     * Otherwise, the priority of this thread is set to the smaller of        不然,此線程的優先級將設置爲指定的<code> newPriority </ code>和該線程的線程組的最大容許優先級中的較小者。
     * the specified <code>newPriority</code> and the maximum permitted
     * priority of the thread's thread group.
     *
     * @param newPriority priority to set this thread to                    參數 newPriority優先級將此線程設置爲@exception IllegalArgumentException若是優先級不在範圍<code> MIN_PRIORITY </ code>至
     * @exception  IllegalArgumentException  If the priority is not in the      <code> MAX_PRIORITY </ code>。
     *               range <code>MIN_PRIORITY</code> to
     *               <code>MAX_PRIORITY</code>.
     * @exception  SecurityException  if the current thread cannot modify     若是當前線程沒法修改此線程報SecurityException。
     *               this thread.
     * @see        #getPriority
     * @see        #checkAccess()
     * @see        #getThreadGroup()
     * @see        #MAX_PRIORITY
     * @see        #MIN_PRIORITY
     * @see        ThreadGroup#getMaxPriority()
     */
    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);
        }
    }

    /**
     * Returns this thread's priority.    獲取屬性
     *
     * @return  this thread's priority.
     * @see     #setPriority
     */
    public final int getPriority() {    
        return priority;
    }

4)setDaemon和isDaemon

  用來設置線程是否成爲守護線程和判斷線程是不是守護線程。

  守護線程和用戶線程的區別在於:守護線程依賴於建立它的線程,而用戶線程則不依賴。舉個簡單的例子:若是在main線程中建立了一個守護線程,當main方法運行完畢以後,守護線程也會隨着消亡。而用戶線程則不會,用戶線程會一直運行直到其運行完畢。在JVM中,像垃圾收集器線程就是守護線程。

/**
     * Marks this thread as either a {@linkplain #isDaemon daemon} thread    將此線程標記爲{@linkplain #isDaemon守護程序}線程或用戶線程。 當全部正在運行的線程都是守護程序線程時,Java虛擬機將退出。
     * or a user thread. The Java Virtual Machine exits when the only
     * threads running are all daemon threads.
     *
     * <p> This method must be invoked before the thread is started.    必須在啓動線程以前調用此方法。
     *
     * @param  on
     *         if {@code true}, marks this thread as a daemon thread    若是{@code true}將此線程標記爲守護線程
     *
     * @throws  IllegalThreadStateException                              若是此線程是{@linkplain #isAlive alive},則@@拋出IllegalThreadStateException
     *          if this thread is {@linkplain #isAlive alive}
     *
     * @throws  SecurityException
     *          if {@link #checkAccess} determines that the current        若是{@link #checkAccess}肯定當前線程沒法修改該線程,則@throws拋出SecurityException
     *          thread cannot modify this thread
     */
    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;
    }

關於用戶線程和守護線程

在java多線程開發中,有兩類線程,分別是User Thread(用戶線程)和Daemon Thread(守護線程) 。

用戶線程很好理解,咱們平常開發中編寫的業務邏輯代碼,運行起來都是一個個用戶線程。而守護線程相對來講則要特別理解一下。

守護線程,相似於操做系統裏面是守護進程。因爲Java語言機制是構建在JVM的基礎之上,這一機制意味着Java平臺是把操做系統的進程給屏蔽了。因此須要在JVM裏面構造出對本身有利的機制,因而守護線程應運而生。

所謂的守護線程,指的是程序運行時在後臺提供的一種通用服務的線程。好比垃圾回收線程就是一個很稱職的守護者,而且這種線程並不屬於程序中不可或缺的部分。所以,當全部的非守護線程結束時,程序也就終止了,同時會殺死進程中的全部守護線程。反過來講,只要任何非守護線程還在運行,程序就不會終止。

事實上,User Thread(用戶線程)和Daemon Thread(守護線程)從本質上來講並無什麼區別,惟一的不一樣之處就在於虛擬機的離開:若是用戶線程已經所有退出運行了,只剩下守護線程存在了,虛擬機也就退出了。由於沒有了被守護者,守護線程也就沒有工做可作了,也就沒有繼續運行程序的必要了。

守護線程並不是只有虛擬機內部能夠提供,用戶也能夠手動將一個用戶線程設定/轉換爲守護線程。

在Thread類中提供了一個setDaemon(true)方法來將一個普通的線程(用戶線程)設置爲守護線程。

相關文章
相關標籤/搜索