目录
实现多线程的官方正确方法:2种
实现Runnable接口方式的实现原理
两种方法的对比
匿名内部类实现线程的两种方式
思考:同时用两种方法会怎么样
总结:最精准的描述
实现多线程的官方正确方法:2种
- 方法一:实现Runnable接口
- 声明实现Runnable接口的类,实现Runnable接口中仅有的run方法,然后分配实例对象,在创建Thread时作为一个参数来传递并启动
- 具体步骤:
- 1,定义类实现Runnable接口
- 2,在该类中实现Runnable接口中的
run()
方法 - 3,线程中具体要执行的东西写在
run()
方法中 - 4,创建Thread类的对象,并在该对象中传入该
实现Runnable
接口的对象作参数 - 5,Thread类的对象调用
start()
方法开启新线程,其内部会自动调用run方法
- 方法二:继承Thread类
- Thread类实现了Runnable接口,在java.lang包下
- 创建执行线程方法二:将类继承Thread类,重写Thread类的run方法
- 接下来就可以分配并启动该子类的实例
- 具体步骤:
- 继承Thread类
- 重写run方法
- 将执行的代码写在run方法中
- 创建Thread类的子类对象
- 使用start方法开启线程
- 注意:调用run方法不能开启多线程
- 只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行
- 如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码
实现Runnable接口方式的实现原理
- 1、查看Thread 类的
构造函数
,传递了Runnable接口的引用,直接调用了init
方法 - 2、追踪
init
方法,在init方法体中找到了传递的target
参数,赋值给了Thread类的Runnable接口的成员变量的target
- 3、查看run方法时,发现run方法中有判断,如果target不为null就会调用实现Runnable接口子类对象的run方法
两种方法的对比
- 方法一(实现Runnable接口)更好
- 继承Thread类是不推荐的,因为它有以下一些缺点:
- 1-从代码架构角度:具体的任务(run方法)应该和“创建和运行线程的机制(Thread类)”解耦,用runnable对象可以实现解耦
- 2-使用继承Thread的方式的话,那么每次想创建一个新任务,只能新建一个独立的线程,而这样做的损耗会比较大(比如重头开始创建一个线程,执行完毕以后再销毁等;如果线程的实际工作内容,就是run方法里面只是简单的打印一行字的话,那么可能线程的实际工作内容还不如损耗来的大);如果使用Runnable和线程池,就可以大大减少这样的损耗
- 3-继承Thread类后,由于Java语言不支持双继承,这样就无法再继承其他的类,限制了可扩展性
- 两种方法的本质对比
- 方法一和方法二,也就是“实现Runnable接口并实现run方法”和“继承Thread类然后重写run方法”
在实现多线程的本质上并没有区别
,最终都是调用了start()方法来启动线程 - 这两个方法的
最主要区别在于run方法的内容来源
:
- 方法一:最终调用target.run()
- 方法二:run()整个都被重写了
- 源码中的区别:
- 继承Thread类方式:由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法(Java虚拟机自动完成)
- 实现Runnable方式:构造函数中传入了Runnable的引用,传给了Thread类中的成员变量,start()调用了run()方法时的内部判断成员变量Runnable的引用是否为空,若不为空,编译时看的是Runnable的run(),运行时执行的是具体实现类中的run()
- 优缺点:
- 继承Thread类方式
- 好处:可以直接使用Thread类中的方法,代码简单
- 弊端:同样也是面向对象中的继承的缺点:
如果该具体类已经有了其他的父类,那么就不能多重继承Thread类
,就不能使用这种方法;此时面向接口编程
的优势脱颖而出
- 实现Runnable接口方式
- 好处:即继承的弊端:即使自己定义的
线程类有了其他父类也可以实现该Runnable接口
;Java中的接口是多实现
的,继承是单继承
,比较有局限性 - 弊端:
不能直接使用Thread类中的方法
,需要先把Runnable具体实现类对象传递给Thread类并获取到线程对象
后,才能得到Thread类的方法,代码相对复杂
- 好处:即继承的弊端:即使自己定义的
- 继承Thread类方式
匿名内部类实现线程的两种方式
- 即直接使用匿名内部类的方式简化代码:
- 实现Runnable接口方式:
Runnable
接口是一个函数式接口
,可以直接用Lambda表达式
代替- 继承Thread类方式:
思考:同时用两种方法会怎么样
- 示例:
- 执行结果:
- 从面向对象的思想去考虑,其实就是Runnable方式run()被实现,但是实现又被Thread方式run()方法给覆盖了,所以只打印了Thread方式run()方法里面的内容
总结:最精准的描述
- 通常我们可以分为两类,Oracle也是这么说的
- 准确的讲,创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元有两种方式
- 方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类
- 方法二:重写Thread的run方法(继承Thread类)