前言
Oracle官方文档上表明,有两种方法可以创建新的线程。
- 将类声明为Thread的子类(继承Thread),该子类重写Thread类的run方法。
- 类实现Runnable接口,进而实现run方法。在创建Thread类的同时,作为构造参数传递给Thread。
应用
/**
* @Description: 继承thread类实现
* @Author: zlt
* @Date: 2021/1/18 22:06
*/
public class ThreadStyle extends Thread{
@Override
public void run(){
System.out.println("用Thread类实现线程");
}
public static void main(String[] args) {
new ThreadStyle().start();
}
}
/**
* @Description: 实现Runnable接口实现
* @Author: zlt
* @Date: 2021/1/18 22:05
*/
public class RunnableStyle implements Runnable{
public static void main(String[] args) {
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
@Override
public void run() {
System.out.println("用Runnable方法实现线程");
}
}
Thread有两类构造函数,一个构造函数参数是Runnable实现类作为参数;一个构造函数是空构造,Thread子类直接调用start方法运行的。
继承Thread类来创建线程的缺点:
- 代码架构角度考虑,具体执行任务应该和线程创建、执行、销毁是解耦的
- 继承Thread类,每次新建一个任务,只能新建一个独立的线程,新建独立线程开销比较大,需要维护线程的创建、执行、销毁,如果利用实现Runnable方式,可以使用后续的线程池之类的工具,大大减少创建、销毁线程的带来的损耗
- 继承Thread之后,Java不支持双继承,子类无法继承其他的类,限制了可扩展性
两种方法的本质区别:
- 实现Runnable接口,最终通过当Trhead构造参数调用start方法,进而调用target.run()
- 继承Thread类,导致run()整个都被重写
思考
- 同时使用两种方法会怎样?
/**
* @Description: 两种方式同时执行
* @Author: zlt
* @Date: 2021/1/18 22:35
*/
public class BothRunnableThread {
/**
* 返回值:“我来自Thread”
* 因为父类Thread已经被匿名类重写了,即便Thread类的构造参数传入target不为空,也不会返回:“我来自Runnable”
* @param args
*/
public static void main(String[] args) {
new Thread(new Runnable() {
/**
* 传入Runnable对象
*/
@Override
public void run() {
System.out.println("我来自Runnable");
}
}){
/**
* 匿名内部类重写了Thread的run方法
*/
@Override
public void run(){
System.out.println("我来自Thread");
}
}.start();
}
}
小结
- 创建线程的方式,通常可以分为两类(Oracle也是这样说的)
- 准确的讲,创建线程只有一种方式,那就是构造Thread类,实现线程的执行单元有两种方式。
- 实现Runnable接口的run方法,并把Runnable实例传给Thread
- 重写Thread的run方法(继承Thread类)