Baeldung 翻译系列之Java并发基础:
Java中的concurrent包
Java中的Synchronized关键字
Future介绍
ThreadLocal介绍
Java线程的生命周期
如何杀掉一个Java线程
Java中的线程池介绍
实现Runnable接口还是继承Thread类
Java中的wait和notify方法
Runnable vs Callable
wait和sleep的区别
Thread.join方法介绍
Java中使用锁对象
ThreadPoolTaskExecutor中的corePoolSize和maxPoolSize
Java中的异步编程
1. 介绍
这是一个很常见的问题:“我应该实现 Runnable
还是继承 Thread
类?”在实践中,哪种方法更有意义,我们将在这篇文章中进行探讨。
2. 使用Thread类
我们先等一一个 SimpleThread
类,它继承Thread
类
public class SimpleThread extends Thread {
private String message;
// standard logger, constructor
@Override
public void run() {
log.info(message);
}
}
再看看我们如何使用这个类来运行一个线程
@Test
public void givenAThread_whenRunIt_thenResult()
throws Exception {
Thread thread = new SimpleThread(
"SimpleThread executed using Thread");
thread.start();
thread.join();
}
我们也可以通过使用 ExecutorService 来运行这个线程:
@Test
public void givenAThread_whenSubmitToES_thenResult()
throws Exception {
executorService.submit(new SimpleThread(
"SimpleThread executed using ExecutorService")).get();
}
能够看出来要在一个线程里面运行一个单独的日志操作流程,还挺麻烦的。
另外,注意 SimpleThread
不能继承任何其他类,因为 Java 不支持多重继承。
3. 实现Runnable接口
现在看看我们实现 java.lang.Runnable 接口来做一个简单任务
class SimpleRunnable implements Runnable {
private String message;
// standard logger, constructor
@Override
public void run() {
log.info(message);
}
}
上述的 SimpleRunnable
只是我们希望在单独线程中运行的一个任务。
我们有多种方法可以运行它;其中之一就是使用 Thread 类:
@Test
public void givenRunnable_whenRunIt_thenResult()
throws Exception {
Thread thread = new Thread(new SimpleRunnable(
"SimpleRunnable executed using Thread"));
thread.start();
thread.join();
}
我们也可以使用一个ExecutorService
:
@Test
public void givenARunnable_whenSubmitToES_thenResult()
throws Exception {
executorService.submit(new SimpleRunnable(
"SimpleRunnable executed using ExecutorService")).get();
}
由于我们现在实现了一个接口,如果需要,我们可以自由地继承另一个基类。
从 Java 8 开始,任何公开单个抽象方法的接口都被视为函数式接口,这使得它可以作为 lambda
表达式的目标。
我们可以使用 lambda
表达式重写上述的 Runnable
代码:
@Test
public void givenARunnableLambda_whenSubmitToES_thenResult()
throws Exception {
executorService.submit(
() -> log.info("Lambda runnable executed!"));
}
4. Runnable 还是 Thread ?
简单来说,我们通常鼓励使用 Runnable
而不是 Thread
:
- 当继承
Thread
类时,我们并未覆盖其任何方法。相反,我们覆盖的是Runnable
的方法(Thread 恰好实现了它)。这明显违反了IS-A Thread
原则。 - 创建
Runnable
的实现并将其传递给Thread
类使用的是组合而非继承 - 这更加灵活。 - 扩展了
Thread
类后,我们不能再扩展任何其他类。 - 从 Java 8 开始,
Runnable
可以表示为lambda
表达式。
因此,在大多数情况下,实现Runnable
接口比扩展Thread
类更有意义。
5. 总结
这篇简短教程中,我们讨论了为什么实现Runnable接口通常来讲比继承Thread类更好的地方。
译者:补充一点,有些刚入门的朋友喜欢 实现一个Runnable之后,直接调用这个的run方法,比如上面的 new SimpleRunnable().run(),这样执行的内容,并不是在一个线程的上下文中执行的,没有任何意义,它只会在当前主线程中执行run这个方法。
评论区