【多线程】实现线程到底只有一种方式还是四种方式

实现一个线程,我在初学Java的时候,听到老师讲的方式,有三种,继承Thread,实现Runnable,实现Callable。网上有的文章说是四种,多出来一个线程池。而有的则说是只有1种。于是我对于探究线程的实现方式到底有几种有了点兴趣。

1,继承Thread
实现线程的第一种方式,继承Thread类,并重写run方法。

在这里插入图片描述
这一种的没什么好说的。

2,实现Runnable接口
第二种方法,找一个类,实现Runnable接口,重写run方法,并且将这个实现类的实例,丢到new Thread()的构造函数中去。
在这里插入图片描述

从中可以看出,这种方式依然依赖于new Thread()来创建线程,实现了Runnable接口的MyRunnable甚至都没有start方法。MyRunnable只是作为构造函数的参数参与了Thread的构造。
在这里插入图片描述

那么这个MyRunnable做了什么呢。
在这里插入图片描述
一路点下去会在这里发现端倪
在这里插入图片描述
在这里插入图片描述可以看到,实际上将MyRunnable作为new Thread()的参数的作用,就是将Runnable接口赋值给target,指定这个Thread要的运行任务什么。
也就是说,实现Runnable,本质上也只是new Thread

3,实现Callable
第三种方法,实现Callable接口,并重写call方法。Callable与Runnable的区别是Callable有返回值,并且可能抛异常。而Runnable没有返回值。

与Runnable不同,Runnable没有返回值,因此可以用同步的方式使用,start后不会阻塞,可以继续start下一线程或者做其他事。Callable由于有返回值,如果继续使用同步的方式,在返回结果之前会阻塞,如果此时需要建立多个线程,那么就要一个一个的等待线程执行结束。而现在的计算机都是多核cpu,支持多线程同时操作。因此Callable常与异步Future结合使用。这里的演示也是结合了Future进行演示(FutureTask是Tuture接口的实现)。

在这里插入图片描述
可以看到,其实Future也是作为一个参数去参与构建Thread,而且构造函数和Runnable的那个是同一个。因为Future实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable接口和Feature接口。换句话说,Future实现了Runnable接口。
在这里插入图片描述
在这里插入图片描述
Runnable方法中已经说到,Runnable构造Thread其实就是通过给Thread中的Runnable target变量赋值,告诉线程run方法要做什么。而在FutureTask中,重写了RunnableFuture的run方法,它的run方法中,会调用call方法,也就是之前实现Callable接口时重写的线程要做的具体的任务的call方法。

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            //在构造FutureTask时会将state置为NEW
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                	//这里执行call方法
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

这样就串联起来了,其实还是通过new Thread,指定Runnable的实现类的引用为target,指定run方法。而FutureTask的run就是调用了call方法。因此我们可以说,这种方法其实还是通过new Thread去构建线程。

4,线程池
线程池这个就比较简单了。我们知道,线程池本身不是一种创建线程的方式,它只是会将创建好的线程进行管理。本质上还是要靠前三种方式去实现线程。因此它也不是实现线程的方式。
在这里插入图片描述

总结
由上述可以得知,真正能创建线程的方式,本质上只有new Thread()一种。不过表现出来不同的方式而已,不同new Thread的方式有三种,普通的new Thread,实现Runnable,实现Callable。不包括线程池,线程池只是管理线程,将通过前三种方式创建好的线程交给你使用并回收,创建线程仍然依赖于前三种方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值