在java.util.concurrent 包出现在Java 5 版本之前,实际上没有其他选择可以进行并发计算,只能直接操作Threads。
您可以直接操作线程,例如通过子类化:
public class MyThread extends Thread {
public void run() {
myComputation();
}
}
或者您可以通过创建Runnable 来做同样的事情,这是首选方式(主要是因为您不需要子类化任何东西,这提供了清晰的关注点分离、更好的代码重用或通常更好的组合) :
public class MyRunnable implements Runnable {
public void run() {
mycomputation();
}
}
new Thread(new MyRunnable()).start();
但无论您使用哪种方式,您都必须创建、启动、操作、加入线程。哪个有缺点:您可以创建多少个线程?这个贵吗? (在某些时候确实如此,尽管随着每个 JVM 实现它变得越来越便宜。)
另外,这个 API 本身有一些开发人员必须克服的限制:如果我想从 Runnable 返回一个值,但看到签名仍然不允许呢?我如何知道我的Runnable 是否因Exception 而失败?诸如异常处理程序之类的东西可以通过线程来实现,但这是一个重复的、容易出错的任务。
进入java.util.concurrent 包!它为所有这些(以及更多)提供了答案!
您希望将线程重用于多个“工作单元”(是否是Runnable?):您可以。想知道“工作单元”是完成还是失败:可以。想要返回一个值:可以。想要动态地将某些任务优先于其他任务?当然。需要安排任务的能力?可以做。以此类推。
用Callables 替换您的Runnables(这很简单,两者都是单一方法接口!),停止操纵Threads 以支持Futures 并将管道留给@987654335 @。
我怀疑 Callable 是否能够做 Runnable 的所有事情,为什么这么多人使用 Runnable 而不是 callable?
也许他们确实有旧代码(Java 1.4?)。也许他们没有意识到更高级别抽象的好处,而是更喜欢低级别的Thread 东西?
一般来说,既然并发 API 来了,绝对没有理由更喜欢使用Threads。
与Runnable接口相比,实现Callable接口是否有额外的开销?
在“实现Callable”与Runnable 中,没有,没有。但是新的Callable 东西是有成本的,因为在内部,这一切都归结为Threads 和Runnables(至少,当前的实现是这样做的)。但您实际上可能会获得性能提升,因为有了更简单的线程重用等新功能!
所以,是的,并发 API 有其成本,但它在任何标准用例中绝对完全可以忽略不计,但它也有新的优势,使其在许多方面都变得更好,包括性能。
此外,您还可以从 API 中获得大量新的可能性,如前所述(参见 Fork/Join 框架,参见 Java 8 中的并行流,...),并减少对任何“自定义”扭结的需求,上述功能的“自制”并发代码,众所周知,这很难做到。
收益/成本比完全有利于“新”并发 API。