实现多线程的方法到底有几种?
![](https://img.haomeiwen.com/i1733340/f40e7fcbdb62308c.png)
关于多线程的实现方式,互联网上的资源鱼龙混杂,有两种的还有三种四种的,最多的还有六种的。但是正确的答案只可能是三种四种或者六种中的其中个,那么Oracle官方是怎么说的呢?
![](https://img.haomeiwen.com/i1733340/9b2cd734b9ee5ee4.png)
一共有两种实现多线程的方法,一种是实现Runnable接口,一种是继承Thread类。
方法一:实现Runnable接口
/**
* 描述: 用Runnable方式创建线程
*/
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类
/**
* 描述: 用Thread方式实现线程
*/
public class ThreadStyle extends Thread{
@Override
public void run() {
System.out.println("用Thread类实现线程");
}
public static void main(String[] args) {
new ThreadStyle().start();
}
}
我们再来看一下源码
![](https://img.haomeiwen.com/i1733340/fd088f3540fcc3ee.png)
当我们使用实现Runnable接口方式创建线程时会传入一个target对象,然后判断不为空执行run方法。继承Thread类创建线程时会重写run方法,子类覆盖父类,没有了判断过程。
方法一(实现Runnable接口)更好。
1.Java只支持单根继承,所以实现Runnable接口更加灵活。方法一最终调用target.run(); 方法二run()整个方法都被重写。
2.方法一传入Runnable实例可以重复利用线程,如果使用方法二则会不断的new Thread。
3.run方法中执行的是具体的任务,线程的声明周期是Thread类的事情。这两个事情目的不同,站在代码架构的角度,应该解耦。
我们来看一种特殊的情况,创建线程的时候既实现了Runnable接口又继承了Thread类。
/**
* 描述: 同时使用Runnable和Thread两种实现线程的方式
*/
public class BothRunnableThread {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我来自Runnable");
}
}) {
@Override
public void run() {
System.out.println("我来自Thread");
}
}.start();
}
}
运行结果是:我来自Thread。因为虽然我们实现了Runnable接口,但是重写了run方法,上边Thread类的三行代码被覆盖。最终执行的只有我们重写的run方法。
方法的总结
实现多线程通常我们可以分为两类,Oracle也是这么说的。准确的讲,创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元有两种方式。
方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类。
方法二:重写Thread的run方法(继承Thread类)。
典型的错误观点分析
线程池创建线程也算是一种新建线程的方式
线程池的内部线程工厂也是使用new Thread()的方法创建线程的。
通过Callable和FutureTas看创建线程,也算是一种新建线程的方式。
通过查看类的关系,本质上也是实现Runnable接口的。
无返回值是实现runnable接口,有返回值是实现callable接口,所有callable是新的实现线程的方式。
和上边一样,并没有创新。
定时器
和线程池的观点并没有不同。
匿名内部类
Lambda表达式
只是表象不同,本质都是利用两种方法创建线程。多线程的实现方式在打吗中写法千变万化,但其本质万变不离其宗。
网友评论