經典面試題:線程中run()方法和start()的區別

結論

話不多說,先上結果:線程直接調用run()方法就相當於一個普通對象調用的他的方法,而只有調用start()方法,線程纔會啓動,此時才具有搶佔CPU資源的資格。當某個線程搶佔到 CPU 資源後,會⾃動調⽤ run ⽅法。

分析

這篇博客不僅會講解標題中的問題,還會說到關於線程的知識,所以我是重頭分析的。

1、定義線程的常用的兩種方式:

1、繼承Thread 2、實現Runable接口。
(1)Thread的源碼分析
在這裏插入圖片描述可見Thread也是繼承了Runable接口的,並且有一個run()方法。
在這裏插入圖片描述
target是什麼呢?
在這裏插入圖片描述
就是一個任務。
(2)Runable接口源碼
在這裏插入圖片描述
很簡單,就是一個抽象的run方法。
小結:線程是去搶佔 CPU 資源的,任務是具體執⾏業務邏輯的,線程內部會包含⼀個任務,線程啓動(start),當搶佔到資源之後,任務就開始執⾏(run)。

2、代碼演示-自定義線程

1、繼承Thread:

package com.demo.Thread;

/**
 * @author wd
 * @version 1.0
 * @date 2020/3/12 23:08
 */
public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i=0; i<10; i++){
            System.out.println("我是李四");
        }
    }
}

2、實現Runable

package com.demo.Thread;

/**
 * @author wd
 * @version 1.0
 * @date 2020/3/12 23:46
 */
public class MyRunable implements Runnable {

    @Override
    public void run() {
        for (int i=0; i<10; i++){
            System.out.println("我是王五");
        }
    }
}

3、寫個測試類:
(1)測試兩種方式的區別

繼承Thread的線程

public class threadTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
   }
}

輸出結果:
在這裏插入圖片描述
實現Runable接口

public class threadTest {
    public static void main(String[] args) {
        /*MyThread thread = new MyThread();
        thread.start();*/
        MyRunable task = new MyRunable();
        Thread runThread = new Thread(task);
        runThread.start();
   }
}

輸出結果:
在這裏插入圖片描述

小結

  1. MyThread,繼承 Thread 類的⽅式,直接在類中重寫 run ⽅法,使⽤的時候,直接實例化MyThread,start 即可,因爲 Thread 內部存在 Runnable。
  2. MyRunnbale,實現 Runnable 接⼝的⽅法,在實現類中重寫 run ⽅法,使⽤的時候,需要先創建Thread 對象,並將 MyRunnable 注⼊到 Thread 中,Thread.start。
    實際開發中推薦使⽤第⼆種⽅式。因爲低耦合,在這裏即把線程和任務分開了。

(2)Thread中run方法和start方法的區別
重寫測試類:

public class threadTest {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        thread1.run();
        for (int i =0; i<5; i++){
            System.out.println("我是張三");
        }
   }
}

輸出結果:
在這裏插入圖片描述
按照順序執行的,因爲thread1線程壓根就沒開啓,所有操作都是主線程去做的。
把thread1.run();換成thread1.start();重寫測試類:

public class threadTest {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        thread1.start();
        for (int i =0; i<10; i++){
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我是張三");
        }
    }
}

爲了有效果,讓主線程睡一睡。
輸出結果:
在這裏插入圖片描述
可以看見此時,並不是代碼順序輸出的。因爲thread1線程開啓,會和主線程搶佔資源了。也就是有兩個線程在跑了。
這也就是run和start兩個方法的區別。

奇怪的指數增加了,線程的第三中實現方式

實現 Callable 接⼝
Callable 和 Runnable 的區別在於 Runnable 的 run ⽅法沒有返回值,Callable 的 call ⽅法有返回值。
注意:Callable不可以和Runable一樣直接用Trhead實現。實現方式如下圖:
在這裏插入圖片描述代碼:

public class MyCallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable mc = new MyCallable();
        FutureTask futureTask = new FutureTask(mc);
        Thread thread1 = new Thread(futureTask);
        Thread thread2 = new Thread(futureTask);
        thread1.setName("A");
        thread1.start();
        thread2.setName("B");
        thread2.start();
        for (int i = 0;i < 5;i++){
            System.out.println(i);
            System.out.println(futureTask.get());
        }
    }
}

class MyCallable<String> implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("我是callable要執行的任務");
        return (String) "我是返回的值";
    }
}

在這裏插入圖片描述
從以上代碼的輸出結果可得到的一些結論:

  1. 兩個Thread都應該輸出“我是callable要執行的任務”的,可是隻輸出了一遍,因爲Callable是有緩存的,所以任務只執行一遍。
  2. 代碼中有三個線程,最先執行主線程,主線程中要拿futureTask的值,所以他停了,等待Callable線程返回值後再繼續向下執行。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章