为啥使用线程池而不使用new Thread(runnable).start();

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为啥使用线程池而不使用new Thread(runnable).start();相关的知识,希望对你有一定的参考价值。

参考技术A 1、每次new Thread,新建对象性能差
2、缺乏统一管理,可能导致线程创建过多,死机等。
3、缺乏更多功能,如:定时执行,定期执行,线程中断等。
4种线程池:

Java通过Executors提供四种线程池,分别为:
1、newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需求,可以灵活回收空闲线程,若无可回收则新建线程。
2、newFixedThreadPool创建一个定长线程池,可以控制线程最大并发数,超过的线程会在队列中等待。
3、newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。
4、newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。
示例:1、2、3、4

1、newCachedThreadPool

2、newFixedThreadPool

3、newScheduledThreadPool

4、newSingleThreadExecutor

Thread为啥要实现Runnable?

【中文标题】Thread为啥要实现Runnable?【英文标题】:Why does Thread implement Runnable?Thread为什么要实现Runnable? 【发布时间】:2013-08-20 18:52:48 【问题描述】:

当线程启动时,JVM 会在该线程上调用 Java 线程的 run() 方法。要给线程做点什么,你可以创建一个 Thread 的子类并覆盖它的 run() 方法,或者(首选)你可以为线程的构造函数提供一个 Runnable。没关系。

我正在创建 Thread 的子类并覆盖 run,但我意识到我无法像预期的那样使方法受到保护,因为 Thread.run() 是公共的。然后我意识到为什么:它必须是公开的,因为 Thread 实现了 Runnable。但是为什么要实现 Runnable 呢?

这似乎不合逻辑。一个线程是可启动(从当前线程),但你不能以与 run() 一个 Runnable(从当前线程)相同的方式运行它;线程自行运行(在自己的线程上)。如果您确实手动调用了 Thread 的 run 方法,那么您并没有将其用作 Thread,而只是一个重量级的 Runnable。

由于这种设计,任何可以访问 Thread 对象的代码都可以调用其公共 run 方法,并可能会插入不打算公开或设计为以这种方式调用的代码。它还允许像这样非常奇特的事情:

Thread.currentThread.run();

Thread 实现 Runnable 是否有合法用途但我没有看到?

【问题讨论】:

@cHao - 如果你了解 Java 的历史和目标(尤其是运行几十年前的代码的目标),你会意识到他们正在尽人类所能做到的最好, 在这种情况下。 【参考方案1】:

原因是“向后兼容”。

Thread 类起源于 Java 1.0 ... 或更早版本。在那些日子里,Java 没有内部类,因此没有一种轻量级的方法来实现 Runnable 实例。如果您查看那个时代的旧线程示例和教程,通常会看到扩展 Thread 并覆盖 run() 方法的类。

随着时间的推移,人们意识到扩展Thread 并不是一个好主意(出于各种原因)。但是,Thread 设计无法更改,因为这会使旧的 Java 代码与新的 JVM 不兼容。


Thread 实现 Runnable 是否有合法用途但我没有看到?

这取决于你所说的“合法”是什么意思。

早期编写的旧代码不是“非法”的,因为它以旧的方式做事。没有什么“坏”的地方。

可能在某些情况下扩展 Thread 并覆盖 run() 方法确实有意义。例如,您可能希望run() 实现一些特殊机制来将信息传入或传出所提供的Runnable,或者实现一些特殊的异常处理,或者......使线程“可重新启动”。

在某些情况下,您甚至可能希望直接在线程对象上调用run()。例如,如果您收到一些扩展 Thread 的“狗早餐”代码,并且您必须将其转换为在线程池中运行 修改原始代码。您可能会考虑实例化繁琐的线程类并将实例作为可运行对象传递给线程池以运行。 (是的……太可怕了!)

【讨论】:

危害在于,无论是子类Thread,还是子类Runnable并交给Thread的构造函数,都无法阻止代码通过Thread.run公开调用。此外,令人困惑的是,Thread 上同时存在公共 run()start() 方法,并且调用错误的方法似乎仍然有效。另外,虽然我是在添加内部类之后才开始使用 Java,但我不明白缺少它们如何使创建 Thread 的子类比创建 Runnable 的子类更容易。 @Boann ...坦率地说,它没有。但是您的整个论点似乎是基于这些事情在 1990 年代初就已被理解的假设。他们不是。而 that 就是 API 如此的原因。基于 20 多年后见之明的 Java 批评是无关紧要的……除非您打算实现一种新语言。坦率地说,这个例子只不过是一个表面问题。它不会阻止人们编写良好的可维护代码。 嗯,我没想到这个决定这么老了。所以你同意它从来没有真正合法或有用吗?只是历史失误?如果这是真的,那么Thread.run() 应该被标记为@Deprecated (这不会对覆盖它产生任何影响,但它至少会通过直接调用它而不是start() 来阻止人们滥用它,或者像传递线程一样它们是常见的 Runnables)。 ++实际上,在 Eclipse 中重写已弃用的方法似乎是一个警告,尽管这并不是一件坏事。 不,它不应该被弃用。弃用是针对积极有害的事物。此外,从技术上讲,如果不弃用接口中定义的方法,而不在使用接口的所有处弃用它,则从技术上讲是不可能的。处理错误调用run() 而不是start() 的人的正确方法是使用PMD 或Findbugs 之类的工具。【参考方案2】:

覆盖 run 并不能解释为什么它需要公开。

如果这是您的问题,我认为有一个简单的答案:实现接口的方法在 java 中必须始终是公共的。可以使用抽象类对其进行保护,但如果 Thread 是抽象的,您将无法这样使用它。

至于为什么 Thread 首先要实现 Runnable,java 必须有一种方法可以知道线程实际上在哪个部分执行它的工作。他们为此使用 run 方法。他们本可以更清楚地区分仅实现 Runnable 和让 Thread 子类实现它的逻辑,但我认为这是一个小错误,由于历史原因很难改变。

TLDR; AFAIK确实没有一个很好的理由让一个线程实现Runnable,但是它有理由实现某种类似的接口——他们应该只是可能有某种分离的接口,比如ThreadRunnable,而不是使用相同的接口还有其他用途的可运行接口。

还要注意,在现代 Java 上,您可能无论如何都应该使用 Callables 和 FutureTasks 而不是 Threads。 “对我来说,超时集成、适当取消和现代并发支持的线程池对我来说比成堆的原始线程更有用。”,在 *** 上引用 another answer。

【讨论】:

"它有理由实现这样的某种接口"呃,比如? +1 特别提到接口方法在 Java 中必须是公共的。 @ruakh 我没有看到问题中明确提到这一点,至少不是以它应该被否决的方式,但也许这只是我:P @eis:这是不合理的。仅仅因为有一个子类可以/应该覆盖的占位符方法,这并不意味着所述占位符方法应该是公共的,或者在接口中声明。相反:内部挂钩通常公开。 @eis:一点也不。你可以有一个-abstract 保护方法。再次——不合逻辑。你所说的不同事物之间没有联系。

以上是关于为啥使用线程池而不使用new Thread(runnable).start();的主要内容,如果未能解决你的问题,请参考以下文章

继承java.lang.Thread类并重写run方法为啥不可以创建一个新线程呢!为啥?

eclipse中做android游戏如何创建Thread(线程)

Android?线程池(转)

Java并发编程系列-线程的基本使用

java 多线程

当我在 Thread 对象上调用 run() 时,为啥我的 Java 程序会泄漏内存?