线程实现的三种方式其实是同一种?

374 阅读2分钟

从我实习面试开始就有在面试中遇到过这个问题:创建线程有几种方法

记得当时的回答是:在jdk1.5之后有三种创建方式

1.继承Thread类

2.实现Runnable接口

3.实现Callable接口

工作之后回顾这个问题就会想到,这三种方式的不同点在哪呢?所有有了探究的这篇文章

虽然网上已经有很多文章珠玉在前,但是我还是想写一篇我自己的文章,如果写的不好,请见谅

先来实现最基本的创建线程的三种方式吧

1.继承Thread类

/**
 * @author miao
 */
public class ThreadDemo extends Thread {

    private final String threadName;

    public ThreadDemo(String threadName) {
        this.threadName = threadName;
    }

    @Override
    public void run() {

        System.out.println(threadName + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        // 多线程级别调用
        new ThreadDemo("test thread demo:").start();

        // 方法级别的调用
         new ThreadDemo("test thread demo:").run();
    }
}

2.实现Runnable接口

/**
 * @author miao
 */
public class RunnableDemo implements Runnable {

    @Override
    public void run() {
        System.out.println("RunnableDemo:" + Thread.currentThread().getName());
    }

    public static void main(String[] args) {

        Thread thread = new Thread(new RunnableDemo());

        thread.start();
    }
}

3.实现Callable接口

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * @author miao
 */
public class FutureTaskDemo implements Callable<String> {

    @Override
    public String call() throws Exception {

        return "return callable demo";
    }

    public static void main(String[] args) throws Exception {
        
        FutureTask task = new FutureTask(new FutureTaskDemo());
        new Thread(task).start();
        System.out.println(task.get());
    }
}

已经完成了三种创建线程的方式了,那么我们就来思考一下这三种方式的相同点和不同点吧

其实关于最常见面试题的就是:Runnable与Callable的不同点是什么?

一般同学会回答:Runnable无返回值,而Callable有返回值能抛异常

其实,这三种创建线程的方式本质上是一样的!!!!不信的话看我证明吧

首先是第一种方式,继承Thread类,点开这个类我们可以看到,其实Thread实现了Runnable接口,重写了run()方法

第二种方法我们就不说了,因为他本身就是实现的Runnable接口,我们继续来看第三种实现Callable的接口。

首先我们要看的是FutureTask<V>,实现了RunnableFuture<V>接口,再点开RunnableFuture<V>接口看,继承了

Runnable,Future<V>接口,所以说到底还是与Runnable接口有关

其实文章到这里就可以结束了,但是我看到第三种Callable方法中能通过get()获取到返回值,那又是在什么时候set进去的呢?

为此我点开了FutureTaskrun()方法查找结果,答案如下,在调用完call()方法后,执行set(result)进去,所以我们后面能通过get()方法获取到result

结论:线程有三种创建方式,但本质上都是通过实现Runnable接口重写run()方法来的,而Callable的返回值是在成功调用了call()方法后set(result)进去的