三種建立線程的方式詳解。

1.繼承thread類java

2.實現runnable接口。ide

3.實現callable接口。函數

這三種方式具體如何建立如何調用詳見下面代碼,註釋已經寫得很詳細了。例子中分別用這三種方式建立線程並分別啓動了兩個線程。具體區別本身細微體會下。spa

package com.thread.Lone;

/**
 * 建立線程的第一種方法
 * 繼承java.lang.Thread,而後重寫該類的run方法.
 */
public class ThreadExt extends java.lang.Thread {

        public void run(){
            System.out.println(Thread.currentThread().getName()+" start");
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+"  : "+i);
            }
            System.out.println(Thread.currentThread().getName()+" end");
        }

}
package com.thread.Lone;

/**
 * 第二種建立線程的方法,實現Runnable接口
 * 由於java不支持多繼承,那麼假設你的類自己繼承了一個父類
 * 這時候還想建立線程的話,那麼實現java.lang.Runnable就比較合適了。
 */
public class ThreadRunnable implements java.lang.Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" start");
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"  : "+i);
        }
        System.out.println(Thread.currentThread().getName()+" end");
    }
}
package com.thread.Lone;

import java.util.concurrent.Callable;

/**
 * 前面兩種方式,都沒有返回值,那若是咱們但願線程能把執行結果返回到主程序,就須要用到concurrent包裏面的東西
 * java.util.concurrent.FutureTask 和 java.util.concurrent.Callable
 * 這裏須要實現Callable接口,主要是爲了實現call()方法,而call方法就是帶有返回值的。
 * 啓動方式見主程序。
 */
public class ThreadFuture implements Callable{
    @Override
    public Object call() throws Exception {

        System.out.println(Thread.currentThread().getName()+" start");
        int i;
        for(i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"  : "+i);
        }
        System.out.println(Thread.currentThread().getName()+" end");
        //爲了體現Future的get()的等待效果,這裏休息2秒
        Thread.sleep(2000);
        return Thread.currentThread().getName()+" 總共循環了"+i+"次";
    }
}
package com.thread.Lone;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 在這個類裏面咱們會分別啓動不一樣方式建立的線程。
 */
public class TestRun {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //  啓動繼承Thread類的線程
        //  執行這段代碼你能夠發現console打出的log基本每次都不同
        //  甚至有時候ext2的log會輸出在ext1以前,能夠看出線程啓動以後,具體的執行已經和主程序無關,也不會受到其餘線程的影響。
        ThreadExt ext1 = new ThreadExt();
        //這裏有一個好習慣就是,對於你啓動的線程最好命名一下方便排查問題。
        ext1.setName("ThreadExt one");
        ThreadExt ext2 = new ThreadExt();
        ext2.setName("ThreadExt two");
        //經過閱讀源碼能夠發現,最終調用start()方法是去調用一個本地的start0方法
        //private native void start0(); 因爲我不懂C也就不去看了。
        ext1.start();
        ext2.start();

        //下面啓動實現runnable接口的線程
        //啓動方法略有不一樣,由於runnable接口自己是沒有start()方法的。
        //因此要啓動方式是,新建一個Thread對象並把本身的對象當作Target傳進去
        //而在Thread的run()方法的註釋裏面有這麼寫到:當構建Thread的時候target不爲空,調用target的Run,而若是爲空,則什麼都不作。
        ThreadRunnable threadRunnable1 = new ThreadRunnable();
        //這裏可使用帶名字的構造函數
        Thread t1 = new Thread(threadRunnable1,"threadRannable1 ");
        t1.start();
        //匿名方式
        new Thread(threadRunnable1,"threadRannable2 ").start();

        //下面啓動實現Callable的線程
        //和實現Runnable的線程相似,最終的啓動方式也是經過Thread.start來啓動的。
        //可是Thread只接受Runnalbe類型的target,而咱們的類實現的是callable接口,明顯不匹配
        //這時候就須要引入一個FutureTask類,這個類自己實現了runnable可是持有一個callable對象。是一個典型的適配器模式,將一個Callable對象假裝成一個Runnable對象
        ThreadFuture threadFuture  = new ThreadFuture();
        //把threadFuture給傳入到futureTask
        FutureTask futureTask = new FutureTask(threadFuture);
        //這時候,咱們就能夠把持有threadFuture對象的FutureTask當作target傳給Thread對象。
        //經過源碼能夠看到,當start()執行的時候,會走到futureTask的Run方法,而futureTask的run方法,又回去調用ThreadFuture的call方法,而且把返回值保存到FutureTask的outcome屬性上。
         new Thread(futureTask,"ThreadFuture1 ").start();
        //當咱們調用get()的時候,其實會去判斷當前的狀態,若是線程已經跑完(state=1),就拿到outCome給咱們,而若是沒有跑完就會等待跑完。
         System.out.println(futureTask.get());
        //因此若是單獨調ThreadFuture相關代碼的話,就能夠看到不一樣,這裏在ThreadFuture1執行完以前是不會執行ThreadFuture2的,由於get會處在等待狀態,一直等到當前線程執行完成纔會繼續走下面的邏輯.
        //另外須要注意的是,futureTask在線程執行完以後,自己的狀態已經變成Normal了,這時候再次啓動同一個futureTask的時候,其實是什麼都不作的。
        // 例如:new Thread(futureTask,"ThreadFuture2 ").start(); 其實並不會走到ThreadFuture
        //想要新啓動一個線程須要new一個futureTask。
        FutureTask futureTask2 = new FutureTask(threadFuture);
        new Thread(futureTask2,"ThreadFuture2 ").start();
        System.out.println(futureTask2.get());

    }
}
相關文章
相關標籤/搜索