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类。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。