Java中有三种线程创建方式,分别为:
-
继承
Thread
类并重写run()
方法 -
实现
Runnable
接口的run()
方法 -
使用
FutureTask
方式
继承
Thread
类并重写run()
方法
/**
* @Author Hory
* @Date 2020/10/5
*/
public class ThreadTest {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
public static class MyThread extends Thread {
@Override
public void run(){
System.out.println("I am a child Thread");
System.out.println(this);
}
}
}
运行结果:
I am a child Thread
Thread[Thread-0,5,main]
当创建完thread
对象后该线程并没有被启动执行,直到调用了start()
方法后才真正启动了线程。
其实调用start()
方法后线程并没有马上执行而是处于就绪状态
,这个就绪状态是指该线程已经获取了除CPU资源外的其他资源,等待获取CPU资源后才会真正处于运行状态。
一旦run()
方法执行完毕,该线程就处于终止状态。
使用继承方式的优点:
使用继承方式的好处是,在run()
方法内获取当前线程直接使用this
就可以了,无须使用Thread.currentThread()
方法
使用继承方式的缺点:
不好的地方是Java不支持多继承,如果继承了Thread
类,那么就不能再继承其他类。
另外任务与代码没有分离,当多个线程执行一样的任务时需要多份任务代码,而Runable
则没有这个限制。
下面看实现Runnable
接口的run()
方法方式。
实现
Runnable
接口的run()
方法
/**
* @Author Hory
* @Date 2020/10/5
*/
public class RunnableTask implements Runnable {
public static void main(String[] args) {
RunnableTask task = new RunnableTask();
new Thread(task).start();
new Thread(task).start();
}
@Override
public void run(){
System.out.println("I am a child Thread");
System.out.println(this);
}
}
如上面代码所示,两个线程共用一个task
代码逻辑,如果需要,可以给RunableTask添加参数进行任务区分。
另外,RunableTask可以继承其他类。
但是上面介绍的两种方式都有一个缺点,就是任务没有返回值。
下面看最后一种,即使用FutureTask的方式。
使用
FutureTask
方式
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author Hory
* @Date 2020/10/5
*/
public class CallerTask implements Callable<String> {
public static void main(String[] args) throws InterruptedException{
// 创建异步任务
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
new Thread(futureTask).start();
try{
// 等待任务执行完毕,并返回结果
String result = futureTask.get();
System.out.println(result);
}catch (ExecutionException e){
e.printStackTrace();
}
}
@Override
public String call() throws Exception {
return "hello";
}
}
如上代码中的CallerTask
类实现了Callable
接口的call()
方法。
在main
函数内首先创建了一个FutrueTask
对象(构造函数为CallerTask
的实例),然后使用创建的FutrueTask
对象作为任务创建了一个线程并且启动它,最后通过futureTask.get()
等待任务执行完毕并返回结果。
总结
使用继承方式的好处是方便传参,我们可以在子类里面添加成员变量,通过set
方法设置参数或者通过构造函数进行传递,而如果使用Runnable
方式,则只能使用主线程里面被声明为final
的变量。
不好的地方是Java不支持多继承,如果继承了Thread
类,那么子类不能再继承其他类,而Runable
则没有这个限制。
前两种方式都没办法拿到任务的返回结果,但是Futuretask
方式可以。