結論
話不多說,先上結果:線程直接調用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();
}
}
輸出結果:
小結:
- MyThread,繼承 Thread 類的⽅式,直接在類中重寫 run ⽅法,使⽤的時候,直接實例化MyThread,start 即可,因爲 Thread 內部存在 Runnable。
- 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) "我是返回的值";
}
}
從以上代碼的輸出結果可得到的一些結論:
- 兩個Thread都應該輸出“我是callable要執行的任務”的,可是隻輸出了一遍,因爲Callable是有緩存的,所以任務只執行一遍。
- 代碼中有三個線程,最先執行主線程,主線程中要拿futureTask的值,所以他停了,等待Callable線程返回值後再繼續向下執行。