方式一:继承Thread类
继承Thread类,重写run方法
public class Test01 {
public static void main(String[] args) {
new Task01().start();
}
}
class Task01 extends Thread {
@Override
public void run() {
System.out.println("run task01");
}
}
方式二:实现Runnable接口
实现Runnable接口,然后实现run方法
public class Test02 {
public static void main(String[] args) {
new Task02().start();
}
}
class Task02 implements Runnable {
@Override
public void run() {
System.out.println("run task02");
}
}
方式三:FutureTask + Callable
创建FutureTask抽象类对象,构造方法需要传入Callable对象,创建时实现call方法。由于FutureTask也实现了Runnable接口,调用时类似Runnable接口,可以将其作为参数传递给Thread对象来执行。
public class Test03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("run task03");
return "run success!";
}
});
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
执行结果如下:
通过调用FutureTask的get方法监听线程的返回,从打印的时间上看,主线程是等Thread-0线程休眠完1秒才打印的返回结果,故get方法是类似阻塞的,直到线程执行完毕。
总结
- 方法一和方法二本质都是直接执行我们重写的run方法
- 方法三和前两者的区别在于他可以通过FutureTask对象的get方法阻塞的监听线程的返回值,其本质是在Thread执行run方法时调用我们重写的call方法。