1. 问题的引出:实现线程有几种形式?2种?5种?
正确答案:两种
- 实现Runnable接口
- 继承Thread类
1.1 Thread类中的run()
Thread类的源码:
private Runnable target;@Overridepublic 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类。