java创建线程的方式
本文基于JDK11版本探讨下,JAVA创建线程的方式有哪些?我这边先给我的答案,只有两种方式继承THREAD类、实现RUNNABLE接口,ORACLE官方文档也说只有两种方式。
Thread类
Thread类是Java提供的表示线程的类。它是一个具体的类,实现了Runnable接口,并提供了额外的线程控制和管理功能。通过继承Thread类,可以创建自定义线程类。
Runnable接口
Runnable接口是Java定义的一个函数式接口,用于表示可运行的任务。通过实现Runnable接口,可以将任务逻辑封装在run()方法中,并将其传递给Thread对象来创建线程。
Thread与Runnable实现run方法的本质区别
Thread本身是实现了Runnable接口的类,而Runnable只是一个接口。当你创建并启动一个Thread时,它将开启一个新的线程并在这个新线程中执行其run()方法;而创建一个Runnable并将其传递给Thread的构造函数,当Thread启动时,它将在新的线程中执行Runnable的run()方法。
具体代码分析如下:Thread类本身实现Runable接口
public class Thread implements Runnable {
......省略代码
//构造Thread实例传递的runable实例
public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
this(group, target, name, stackSize, null, true);
}
private Thread(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
......省略....
//将runable 实例赋值给this.target字段
this.target = target;
.....省略......
}
@Override
public void run() {
// 代码关键点判断target是否为空
if (target != null) {
target.run();
}
}
}
如果同时实现Runnable的run方法、重写Thread的run方法会发生什么结果?
代码如下:
public class ThreadDemo {
public static void main(String[] args) {
new Thread(()->{
System.out.println("Runnable run");
}){
@Override
public void run() {
System.out.println("Thread run");
}
}.start();
}
}
输出结果:
Thread run
Runnable比Thread实现更好
在Java中,Runnable和Thread都用于实现多线程编程,但是使用Runnable接口来创建线程通常被认为是更好的选择,有以下几个原因:
-
避免继承限制:Java是单继承语言,如果使用Thread类创建线程,就无法再继承其他类。而使用Runnable接口,可以避免这个限制,因为Java允许类实现多个接口。
-
提高代码的组织性:将任务逻辑与线程逻辑分离,使用实现Runnable接口的类作为任务,可以更好地将代码模块化和组织化。这样可以提高代码的可读性、可维护性和可测试性。
-
适应性更强:Runnable接口使得任务代码可以在多个线程之间共享,可以更灵活地在不同线程之间切换任务。而使用Thread类创建的线程,任务代码与线程代码紧密耦合,不够灵活。
-
资源共享和线程池的使用:使用Runnable接口可以更好地支持线程池的使用。线程池是一种重用线程的机制,可以有效地管理线程的生命周期和资源。通过将Runnable任务提交给线程池,可以更好地管理资源并提高系统性能。
-
扩展性和可替换性:如果以后需要更换线程实现的方式,例如使用Executor框架或其他的并发工具,使用Runnable接口更容易进行迁移和替换。而使用Thread类创建的线程,在替换时可能需要修改大量的代码。
总的来说,使用Runnable接口来实现Java线程更好的原因是它提供了更好的代码组织性、可维护性和可扩展性,同时避免了继承限制,并且更适应于资源共享和线程池的使用。
创建线程方式
在Java中,有多种方法可以实现线程。以下是一些常见的java创建线程实现方法:
-
继承Thread类:这是最基本的线程实现方法之一。通过创建一个继承自Thread类的子类,重写run()方法来定义线程的执行逻辑。然后,可以创建该子类的实例并调用start()方法来启动线程。
-
实现Runnable接口:另一种常见的线程实现方法是实现Runnable接口。定义一个类实现Runnable接口,并实现其run()方法。然后,可以创建该类的实例,并将其作为参数传递给Thread类的构造函数,然后调用Thread的start()方法来启动线程。
-
使用Callable和Future:Java提供了Callable和Future接口,允许线程执行并返回一个结果。Callable接口类似于Runnable接口,但它的call()方法可以返回一个值。可以使用ExecutorService的submit()方法来提交Callable任务,并返回一个Future对象,以便获取任务的结果。
-
使用线程池:线程池是一种维护和管理线程的机制。通过使用Java提供的Executor框架,可以创建线程池并将任务提交给线程池来执行。这种方式可以提高线程的效率和性能,并管理线程的生命周期。
-
使用Java并发类:Java提供了一些并发类,如Semaphore、CountDownLatch、CyclicBarrier等,可以帮助在多个线程之间进行同步和协调操作。这些类提供了更高级的线程控制和同步机制。
-
使用定时器:Java提供了Timer类和ScheduledExecutorService接口,可以用于定时执行任务或周期性执行任务。
但是原理来说只有两种方式,通过重写Thread#run方法和实现Runable类#run方法。深入到源码层面上看的话,都是通过Thread类run方法来实现的,其实两种方式本质上都是一样的。
总结
通常创建线程我们可以分为两类。准确的讲,创建线程只有一种方式那就是构造Thread类,而实线程的执行单元有两种方式。
-
实现Runnable接口的run方法,Thread类run方法会调用Runnable实例的run方法
-
重写Thread的run方法(继承Thread类)