【多線程 二】線程創建和啓動的四種方式(以及詳解Thread和Runnable方式的優缺點)

1、繼承Thread

其實也是實現了runnable接口的。

public class Threadextend extends Thread{
    @Override
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.print("A"+i+ "\t");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Threadextend trea=new Threadextend("小強");
        //start 表示開啓線程
        trea.start();
        for(int i=0 ;i <100;i++){
            System.out.print("B"+i +"\t");
        }
        System.out.println(trea.getName());
    }
}

結果如下,隨機打印
在這裏插入圖片描述

2、實現runnable接口

實現Runnable接口比繼承Thread類所具有的優勢:
1. 適合多個相同的程序代碼的線程去共享同一個資源。 (,注意我說的是適合,這裏我從網上看到了一些人的回答,都說thread不能資源共享,可能是斷章取義吧,Thread可以實現資源共享的,只不過是現實開發中它不適合資源共享,因爲它如果想資源共享的話可以將共享的資源設置成靜態的,因爲靜態資源的生命週期是和類綁在一起的,和對象沒有關係。但是這樣做的壞處就是,如果此時有兩個不一樣的任務的話,同時都調用這個類,那麼就不能實現了,因爲靜態變量不能區分這兩個任務了)

2、 可以避免java中的單繼承的侷限性。 (就是說接口的好處了,實現完接口後還可以繼承)

3、增加程序的健壯性,實現解耦操作,代碼可以被多個線程共享,代碼和線程獨立。
從代碼架構角度:具體的任務(run方法)應該和“創建和運行線程的機制(Thread類)”解耦

4、線程池只能放入實現Runable或Callable類線程,不能直接放入繼承Thread的類。

class ThreadRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.print("A"+i+ "\t");
        }
    }
}
 public static void main(String[] args) {
    ThreadRunnable threadRunnable=new ThreadRunnable();
        Thread thread = new Thread(threadRunnable, "小強");
        thread.start();
        for(int i=0 ;i <100;i++){
            System.out.print("B"+i +"\t");
        }
        System.out.println(thread.getName());
    }

結果同上

3、jdk1.5後,實現callable接口

和以上相比,callable更加強大一些
1:相比run()方法,可以有返回值
2:方法可以拋出異常
3:支持泛型的返回值
4:需要藉助FutureTask類,比如獲取返回結果

class ThreadCallable implements Callable<Integer>{
    int result=0;
    @Override
    public Integer call() throws Exception {
        for(int i=0;i<=100;i++){
            System.out.println(i);
            result +=i;
        }
        return result;
    }
}
    public static void main(String[] args) {
        ThreadCallable threadCallable = new ThreadCallable();
        FutureTask<Integer> result= new FutureTask<>(threadCallable);
        new Thread(result).start();
        try {
            System.out.println(result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
4、jdk1.5後,線程池

提前創建好多個線程,放入線程池中,使用時直接獲取,使用完放回池中。可以避免頻繁創建銷燬、實現重複利用。提高響應速度 (減少了創建新線程的時間) 降低資源消耗(重複利用線程池中線程,不需要每次都創建) 便於線程管理。

4.1體系結構

在這裏插入圖片描述
如上圖,頂級接口爲Executor,真正的線程池接口是ExecutorService,下面是對上圖的說明
在這裏插入圖片描述

4.2 工具類:Executors

Executors.newCachedThreadPool():創建一個可根據需要創建新線程的線程池

Executors.newFixedThreadPool(n); 創建一個可重用固定線程數的線程池

Executors.newSingleThreadExecutor() :創建一個只有一個線程的線程池

Executors.newScheduledThreadPool(n):創建一個線程池,它可安排在給定延遲後運
行命令或者定期地執行。 返回值類型爲ScheduleThreadPoolExeccutor。

class ThreadRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.print(Thread.currentThread().getName()+ "\t"+ "A"+i+ "\t");
        }
    }
}

線程池與Runnable的使用方式

 public static void main(String[] args) {
        // 創建線程池
         ExecutorService pool = Executors.newFixedThreadPool(5);

         ThreadRunnable trb=new ThreadRunnable();
        //爲線程池分配任務
        for(int i=0;i<10;i++){
            pool.submit(trb);
        }
        //關閉線程池,shutdown只是將線程池的狀態設置爲SHUTWDOWN狀態,
        //正在執行的任務會繼續執行下去,沒有被執行的則中斷。而shutdownNow
        //則是將線程池的狀態設置爲STOP,正在執行的任務則被停止,沒被執行任務的則返回。
        pool.shutdown(); 
 }

線程池與Callable的使用方式, 帶有返回值Future

 // 創建線程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        List<Future<Integer>> list = new ArrayList<>();

        //爲線程池分配任務
        for(int i=0;i<10;i++){
            Future<Integer> future = pool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum=0;
                    for (int j = 0; j <100 ; j++) {
                        sum+=j;
                    }
                    return sum;
                }
            });
            list.add(future);
        }
        //關閉線程池
        pool.shutdown();
        for (Future<Integer> future : list) {
            System.out.println(future.get());
        }
5、延申:start() 和run()有什麼區別呢?

run():只是調用了一個普通方法,並沒有啓動另一個線程,程序還是會按照順序執行相應的代碼。線程被調度的時候,執行的操作
start():重新開啓一個線程,此線程進入到就緒狀態,不必等待其他線程運行完,只要得到cup資源就可以運行該線程,如下圖,這個圖在後面的博文中會有詳細的講解

在這裏插入圖片描述

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