java 线程创建原理_【Java】基础32:一道面试题,弄懂线程的创建原理

a14f3132eccf785e76f72772c2055da8.png

一、线程面试题分析

昨天学习了使用匿名内部类创建线程的两种方式:

现在有一道和其相关的面试题。

如果你能够回答上来,那么线程的创建原理你算是完全弄明白了;

如果你不能回答上来,那么希望通过今天对这道面试题的分析让你完全弄明白;

代码在下图,问打印的是刘小爱,还是刘大爱?

可以花个十秒钟,先看看代码,不用着急往下看哈。

704c4f3748ced968eb73db5c18011231.png

老实说,当我看到这个题目的时候,我的内心是拒绝的,有一种一拳打在棉花上,有力无处使的感觉。

具体什么意思呢?

就是每一个单词我都认识,代表着什么意思我也能说出来:Thread是一个类,Runnable是一个接口,然后还有两个重写的run方法,最后就是线程开启的start方法。

很简单吧,没有一个不认识的,但它们全部合在一起,看得我就很懵逼了。

什么原因呢?我想大概还是因为我自己写的代码太少了,见识的也少,自然有的代码也就看不明白了。

所以我决定,将其一步一步地拆分:

24933acacccab5a1c177775053913132.png

以上就是将第一张图拆分后的代码,

第一张图:没有给对象定义一个名字,就直接使用了(这也称之为匿名对象),代码看起来就不是很清楚;

第二张图:我将对象都定义了一个名字,然后再使用这个名字,相对而言看起来就清晰一些。

我们来分析第二张图:

①这是一个Runnable的实现类对象

使用的就是匿名内部类。

Runnable是一个接口,它是没法实例化创建对象的,所以需要它的实现类创建对象,但不想再重新定义一个类了,就可以直接使用匿名内部类。

因为我没有名字,我就用我的父接口的名字来代替。

②这是一个Thread的子类对象

使用的也是匿名内部类。

Thread thread=new Thread(run);

如果是这样写的,那么创建的是啥?

这是创建对象的基本格式,是Thread类的对象。

但后面有一个大括号了,也就是说我所创建的是Thread的子类对象,但不想再重新定义一个类,我就用我的父类的名字代替。

说白了这道面试题就是将创建线程的两种方式结合起来了。

想彻底弄明白这道题目,我们就得去研究下run方法到底是怎么回事。

二、Thread中的run方法

start方法一被调用,代表着啥?

代表着线程启动了,同时Java虚拟机会调用run方法,执行run方法里的内容。

这也很好理解,不然线程只是启动了,没输出,要它有何用?

我们看下Thread的run方法源码:

83ed5b2b473bb97e8d711f00090726db.png

这就是Java中Thread类的run方法,这已经是Java内部开发人员编写好的,我们没法修改。

那么现在问题来了,target是啥呢?

还记得昨天说过的Thread类的4种构造方法么?

回顾一下:

Thread():无参构造,创建一个线程对象。

Thread(String name):给线程对象指定一个名字。

Thread(Runnable target):分配一个指定的线程对象。

Thread(Runnable target,String name):分配一个指定的线程对象并指定名字。

看到没有,构造方法里面就有target。

也就是说,我们创建了一个Runnable的实现类对象run,我们将它作为参数传进了Thread里面,它就是target。

现在明白Thread的run方法了吧:

①如果Thread对象在创建时没有初始化赋值:

那么它的run方法里是没有输出的(因为并没有传值给target),也就是光启动了一个线程,啥都没有。

所以,通过Thread的子类创建线程这种方式,本质是在重写Thread的run方法,让它有了输出内容。

②如果Thread对象在创建时初始化赋值了:

传进去的参数是我们创建的一个Runnable的实现类对象。那么Thread执行的run方法就是我们传进去的参数的run方法,target.run()就是执行Runnable的实现类的run方法。

所以:通过Runnable的实现类创建线程这种方式,本质上是在给Thread类中的run方法填充内容,Thread的run方法就是在调用我的run方法。

它并没有重写Thread的run方法,重写的是接口Runnable的方法。

③现在我们回过头来看一开始的面试题:

拆分后的代码图,再仔细看看:

24933acacccab5a1c177775053913132.png

①是啥?

①是作为参数给Thread初始化赋值了的(也就是那个target)。

现在Thread中的run方法是啥?

还记得源码中的内容么?:target.run(),这就是在调用①中的方法呀。

也就是说,现在Thread的run方法执行的就是①中的run方法。

②是啥?

②是Thread的子类对象,它干了一件什么事情?它重写了Thread的run方法。

好现在问题来了:

①是相当于给Thread中的run方法填充内容。

②是在重写Thread的run方法。

运行结果是啥?

父类引用指向子类对象,在调用方法时,执行的是子类重写后的方法,这是多态里的知识点,也就是说②将①给覆盖了。

还记得那句口诀么?编译看左边,运行看右边

②中左边是啥?Thread引用,也就是父类引用。

②中右边是啥?Thread的子类对象。

编译看左边,左边是Thread类引用,它有start方法么?它有,所以编译不报错,能运行。

运行看右边,右边是Thread的子类对象,所以运行时执行的就是子类对象的方法。

好现在做一个小结:

Thread的start方法启动,Java虚拟机会调用Thread的run方法,输出内容。

继承Thread重写run方法,重写的就是Thread的run方法。

实现Runnable接口重写run方法,这个run方法和Thread的run方法没有直接联系,但是如果Runnable的实现类对象作为构造参数传递给Thread了(也就是target),Thead的run方法就会调用Runnable的实现类重写的run方法(target.run())

总结

0ee320640af801ab515df3d568c6eee0.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值