使用线程
有三种使用线程的方法:
- 实现 Runnable 接口;
- 实现 Callable 接口;
- 继承 Thread 类。
实现 Runnable
和 Callable
接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread
来调用。可以理解为任务是通过线程驱动从而执行的。
实现 Runnable 接口
需要实现接口中的 run() 方法。
public class MyRunnable implements Runnable {
@Override
public void run() {
// ...
}
}Copy to clipboardErrorCopied
通过 Runnable 实例创建一个 Thread 实例,然后调用 Thread 实例的 start() 方法来启动线程。
public static void main(String[] args) {
MyRunnable instance = new MyRunnable();
Thread thread = new Thread(instance);
thread.start();
}
实现 Callable 接口
与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask
进行封装。
public class MyCallable implements Callable<Integer> {
public Integer call() {
return 123;
}
}
通过 Callable实例创建一个 FutureTask 实例,通过 FutureTask 实例创建 Thread 实例,然后调用 Thread 实例的 start() 方法来启动线程。
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread thread = new Thread(ft);
thread.start();
System.out.println(ft.get());
}
继承 Thread 类
同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。
public class MyThread extends Thread {
public void run() {
// ...
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
}
实现接口 VS 继承 Thread
一般而言,推荐实现接口,主要是由于大多数情况下,人们并不会特别去关注线程的行为,也不会去改写Thread已有的行为或方法,仅仅是期望执行任务而已。
因此,使用接口的方式能避免引入一些并不需要的东西,同时也不会影响继承其他类,并使程序更加灵活。
Tips:
-
Runnable 与 Thread 不是对等的概念
在Thinking in Java
中,作者吐槽过Runnable
的命名,称其叫做 Task 更为合理。
在 Java 中,Runnable 只是一段用于描述任务的代码段而已,是静态的概念,需要通过线程来执行。而 Thread 更像是一个活体,自身就具有很多行为,能够用来执行任务。 -
仅仅当你确实想要重写(override)一些已有行为时,才使用继承,否则请使用接口。