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. 说法1:Callable接口
  2. 说法2:线程池方式
  3. 说法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类。


丰木
322 声望19 粉丝

遇见超乎想象的自己!


引用和评论

0 条评论