8

我想我“了解”了 Java 多线程的基础知识。如果我没记错的话,你会做一些大工作并弄清楚如何将它分成多个(并发)任务。然后将这些任务实现为Runnables 或Callables 并将它们全部提交给ExecutorService. (所以,首先,如果我错了这么多,请从纠正我开始!!!

其次,我必须想象您在内部实现的代码run()call()必须尽可能“并行化”,使用非阻塞算法等。这就是困难的部分(编写并行代码)。正确的?不正确?

但是我仍然遇到Java并发的真正问题(我猜一般是并发),这是这个问题的真正主题,是:

什么时候最适合多线程?

我从 Stack Overflow 上的另一个问题中看到了一个示例,其中发布者建议创建多个线程来读取和处理一个巨大的文本文件(Moby Dick书),一位回答者评论说,用于从磁盘读取的多线程是一个可怕的主意。他们这样做的理由是,在已经很慢的进程(磁盘访问)之上,您将有多个线程引入上下文切换的开销。

所以这让我开始思考:哪些类型的问题适合多线程,哪些类型的问题应该始终序列化?提前致谢!

4

7 回答 7

8

多线程有两个主要优点,IMO:

  • 能够在多个 CPU/内核之间分配密集型工作:不是让 4 个 CPU 中的 3 个空闲并在单个 CPU 上完成所有工作,而是将问题分成 4 个部分,让每个 CPU 独立工作。这减少了执行 CPU 密集型任务所需的时间,并证明您花在多 CPU 硬件上的钱是合理的
  • 减少许多任务的延迟。假设有 4 个用户向 Web 服务器发出请求,并且请求都由单个线程处理。假设第一个请求进行了很长的数据库查询。线程处于空闲状态,等待查询完成,其他 3 个用户等待此请求完成以获取他们的小网页。如果你有4个线程,即使只有一个CPU,第二个,第三个和第四个请求可以在数据库服务器执行长数据库查询的同时处理,所有用户都很高兴。因此,当您有阻塞 IO 调用时,多线程尤其重要,因为这些阻塞 IO 调用让 CPU 空闲,而不是执行一些其他等待任务。

注意:从多个线程读取同一个磁盘的问题在于,不是顺序读取整个长文件,而是在每次上下文切换时强制磁盘在磁盘的各个物理位置之间切换。由于所有线程都在等待磁盘读取完成(它们受 IO 限制),这使得读取速度比单个线程读取所有内容要慢。但是一旦数据在内存中,在线程之间拆分工作是有意义的。

于 2012-06-29T17:59:25.687 回答
5

所以这让我开始思考:哪些类型的问题适合多线程,哪些类型的问题应该始终序列化?

基本上 CPU 密集型任务(例如进行大量数据处理,例如内存排序)应该是并行化的(如果可能的话),并且 I/O 绑定任务应该保持顺序(如磁盘 I/O)。这是一般建议,当然也有一些例外。

于 2012-06-29T17:44:27.663 回答
3

所以这让我开始思考:哪些类型的问题适合多线程,哪些类型的问题应该始终序列化?

当您使用 Swing 组件构建 GUI 时,有时您想要通过单击按钮(例如)执行的任务需要很长时间,以至于您在执行任务时会锁定 GUI。

因此,您在不同的线程中执行任务,这样您就可以保持 GUI 线程(Swing 工作线程)响应 Swing 组件。

于 2012-06-29T17:48:50.963 回答
3

多线程对以下方面很有价值:

  • 跨多个 CPU 或多个内核分配工作。
  • 在执行冗长的任务(例如,加载文件、执行计算)时保持响应。
  • 在执行可能阻塞的操作(例如,从套接字读取)时保持响应。
于 2012-06-29T17:52:59.150 回答
1

我更喜欢这种方式......

  1. 对于基于 GUI 的应用程序,线程非常重要。

  2. 在 Java 中,GUI 由 Event Dispatcher Thread 处理。始终建议保持 UI 在 UI 线程上工作,而非 UI 在非 UI 线程上工作。 假设您按下一个按钮,然后向某个 Web 服务器发出一个 http 请求,在服务器上进行处理,然后它以结果响应。如果您不创建非 UI 线程来处理此作业,那么您的 GUI 将除非收到网络服务器的响应,否则为非响应。

  3. 在要同时完成多项工作的情况下,线程也非常重要。 最好的例子是操作系统。通常我一边编码一边听我最喜欢的音乐,同时上网等等。现在这就是多线程非常方便的地方,如果只有一个线程,我们永远不会想象我们能做的事情今天做操作系统。

  4. 跨 CPU 的多线程用于并行处理CPU 密集型工作。

  5. Java Servlet的情况下,每个到达服务器的请求都将由容器提供的单独线程处理。

于 2012-06-29T18:03:58.353 回答
0

并发在某些算法中也非常有用。例如,我目前正在编写一个程序,该程序将使用遗传算法计算复杂问题的最佳解决方案。在遗传算法中,你有一群人,他们都必须执行适应度函数。这些体能测试的执行通常会彼此完全独立,并且会执行很多测试(例如,您可能有数百个人口规模)。并行化可以通过减少执行所有适应度函数所需的时间来显着提高遗传算法的速度。

希望这能让您了解人们在谈论“cpu 密集型”任务时所指的内容,尤其是因为并非所有 cpu 密集型任务都可以轻松并行。

于 2012-06-29T18:25:40.890 回答
0

你机器上 99.9% 的线程没有做任何 CPU 密集型工作。我的盒子目前有 1084 个线程和 1% 的 CPU 使用率——1084 个线程根本没有做任何重要的事情。它们都在等待,许多等待来自其他线程的信号,但最重要的是,许多等待 I/O。在抢占式多任务操作系统上使用多线程的最重要和普遍的原因是提高应用程序的整体 I/O 性能。这些抢占式内核迫使我们陷入同步、队列、锁等的痛苦——本质上是一个不同的设计区域,一条指令不再必须跟随另一条指令。好处是,这是一个巨大的好处,就是 I/O 性能比任何协作调度系统都要好得多,因为任何等待 I/O 驱动程序的线程都可以“立即”准备好/运行 在响应硬件中断的驱动程序完成 I/O 时。异步 I/O 不会改变这一点,它只是将 I/O 等待移动到内核线程池,该线程池必须列出异步请求并在发生 I/O 时使回调设置用户线程准备好,(同时强制用户代码恢复到显式状态机)。所以'什么类别的问题适合多线程':

1) 任何期望 I/O 来自多个源的地方,其中完成可以异步发生。

2) 线程使应用程序设计更容易、更快、更安全。如果必须同时发生 20 件“事情”,那么编写明显的“内联”代码并使用 20 个线程运行它比自己开发一个状态机来处理 20 个不同的上下文要容易得多。由于进程内的线程共享内存,因此在队列上通信巨大的缓冲区(好的,缓冲区引用/指针)是微不足道的,从而简化了分层/流水线应用程序,例如。通讯堆栈。

3)多核机器上的CPU密集型操作,特别是可以隔离每个线程/核心的数据集以进行缓存优化。

4)AOB :)

如果没有多线程和抢占式内核的 I/O 性能,就不会有 BitTorrent、视频流、MMP 游戏和 AVI 播放器。

但是,您将能够运行记事本和 MS Word...

于 2012-06-30T00:32:12.533 回答