从我在 Java 中使用线程的时间开始,我发现了这两种编写线程的方法:
使用implements Runnable
:
public class MyRunnable implements Runnable {
public void run() {
//Code
}
}
//Started with a "new Thread(new MyRunnable()).start()" call
或者,使用extends Thread
:
public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
//Code
}
}
//Started with a "new MyThread().start()" call
这两个代码块有什么显着差异吗?
是的:实现Runnable
是首选方法,IMO。你并不是真正专注于线程的行为。你只是给它一些东西来运行。这意味着构图是哲学上 “更纯粹” 的方式。
实际上 ,它意味着您可以实现Runnable
并从另一个类扩展。
tl; dr:实现 Runnable 更好。但是,警告很重要
一般来说,我建议使用像Runnable
而不是Thread
这样的东西,因为它允许你保持你的工作只与你选择的并发性松散耦合。例如,如果你使用Runnable
并稍后决定这实际上不需要它自己的Thread
,你可以调用 threadA.run()。
警告:在这里,我强烈反对使用原始线程。我更喜欢使用Callables和FutureTasks (来自 javadoc:“可取消的异步计算”)。超时,正确取消和现代并发支持的线程池的集成对我来说比成堆的原始线程更有用。
后续:有一个FutureTask
构造函数 ,允许您使用 Runnables(如果这是您最熟悉的),并且仍然可以获得现代并发工具的好处。引用 javadoc:
如果您不需要特定结果,请考虑使用以下形式的结构:
Future<?> f = new FutureTask<Object>(runnable, null)
因此,如果我们用threadA
替换他们的runnable
,我们得到以下内容:
new FutureTask<Object>(threadA, null)
允许您更接近 Runnables 的另一个选项是ThreadPoolExecutor 。您可以使用execute方法传入 Runnable 以在将来某个时间执行 “给定任务”。
如果您想尝试使用线程池,上面的代码片段将变为如下所示(使用Executors.newCachedThreadPool()工厂方法):
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());