1. 問題的引出:實現執行緒有幾種方式?2種?5種?
正確答案:兩種
- 實現Runnable介面
- 繼承Thread類
1.1 Thread類中的run()
Thread類的原始碼:
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
Thread類有個Runnable的target引用,如果構造器傳入了target且不為null,就執行它的run();但前提是它有機會執行--什麼意思呢?
1.2 既實現了Thread又實現了Runnable介面執行誰的run()
且看如下一段程式碼:MyTask類是Runnable介面的實現;MyThread是Thread類的子類;如果初始化MyThread時把MyTask作為Runnable傳入到target,會執行誰的run()?
package com.niewj.basic.createthread;
/**
* 既實現Runnable又繼承Thread
*
* @author niewj
* @description
* @copyright © 2022 niewj.com
* @department 研發
* @date 2023/1/3 23:14
*/
public class RunnableThread {
public static void main(String[] args) {
// 1. 實現了Runnable
MyTask task = new MyTask();
// 2. 實現了Runnable,也繼承了Thread, thread2輸出什麼?
Thread thread = new MyThread(task);
thread.start();
}
// MyThread是繼承Thread的類
static class MyThread extends Thread {
public MyThread(Runnable runnable) {
super(runnable);
}
@Override
public void run() {
System.out.println("==============MyThread.run()====");
}
}
// MyTask是實現Runnable的介面
static class MyTask implements Runnable {
@Override
public void run() {
System.out.println("==============Runnable.run()====");
}
}
}
執行結果如下:可見執行了Thread的run()而不是Runnable的;
這裡比較迷惑的是,不是說target!=null就執行它的run()嗎?關鍵是這裡不是判斷target的問題,而是整個Thread子類的run()方法被子類覆蓋了,沒有機會執行到MyThread父類Thread的run()方法了,這才是關鍵!
==============MyThread.run()====
1.3 簡寫成如下的程式碼再理解一遍:
package com.niewj.basic.createthread;
/**
* 既實現Runnable又繼承Thread
*
* @author niewj
* @description
* @copyright © 2022 niewj.com
* @department 研發
* @date 2023/1/3 23:14
*/
public class RunnableThread {
public static void main(String[] args) {
// 1. 實現了Runnable
Thread thread = new Thread(() -> System.out.println("==============Runnable.run()====")) {
@Override
public void run() {
System.out.println("==============MyThread.run()====");
}
};
// 2. 實現了Runnable,也繼承了Thread, thread2輸出什麼?
thread.start();
}
}
控制檯:
==============MyThread.run()====
是子類run()覆蓋了Thread的run(),所以Thread類的run()中的邏輯都不會執行到。
2. 實現執行緒方式的其他說法
- 說法1:Callable介面
- 說法2:執行緒池方式
- 說法3:定時器類
2.1 Callable介面
Callable介面本身並不會建立執行緒,最終還是借用了Runnable介面來完成執行緒的作用。別忘了Callable介面的用法:
Callable介面要寄生在FutureTask裡
Callable介面要寄生在FutureTask裡才能實現執行緒,最終靠的也是Runnable,因為FutureTask是Runnable介面的子類。所以只能說Callable是借Runnable完成一次復出而已;
還有說Executors裡使用Callable,實際上也是使用ThreadPoolExecutor執行緒池來實現,內部也是FutureTask,本質上也一樣!
2.2 Executors和執行緒池ThreadPoolExecutor
Executors是一個執行緒池的便捷工具類,內部的方法本質上還是透過持了執行緒池ExecutorService的刀來搶的執行緒的劫,而ExecutorService就是執行緒池的抽象介面,它對於執行緒的實現,也是靠執行緒建立工廠,最終在ThreadPoolExecutor裡也是透過Runnable介面和new Thread來完成的。
2.3 定時器類實現執行緒的說法
2.3.1 TimerTask抽象類
TimerTask抽象類本身也是Runnable的實現類,就不用說了,見原始碼:
public abstract class TimerTask implements Runnable {}
2.3.2 Timer類
Timer類中是持有一個TimerThread類,TimerThread類呢是Thread的子類,本質上也是Thread:
private final TimerThread thread = new TimerThread(queue);
class TimerThread extends Thread {}
所以,最終還是隻有兩種方式實現執行緒:實現Runnable介面和整合Thread類。