Java 异步编程——为什么要使用多线程,以及在什么情况下使用多线程?

前言

单线程就是一个工人在搬砖,多线程就是多个工人在搬砖;在相同数量的砖要搬的情况下,多个工人搬砖肯定比一个人搬的快。

那多个工人做事一定比一个工人做事快吗?那不一定,不过大多数情况下是的。多线程只是可以同时接收多个任务,但不一定马上去执行任务。这里要看有多少资源(单核 or 多核)和做什么任务(计算密集型任务 or IO密集型任务)。基于这两个条件进行笛卡尔积形成的场景来分析单线程和多线程的工作效率。

单核:单核处理器指的是只有一个物理核心的处理器。物理核心是处理器中的实际计算单元,能够执行指令和处理数据。在单核处理器中,只能同时执行一个线程或进程。即使使用多线程编程,也只能通过时间片轮转的方式在不同线程之间进行切换,每个线程在某个时间片内独占处理器的执行时间。

多核:多核处理器指的是具有多个物理核心的处理器。多核处理器将多个物理核心集成到一个芯片上,每个核心都是一个独立的计算单元,能够并行执行指令和处理数据。每个核心可以独立地执行一个线程或进程,因此多核处理器可以同时执行多个线程或进程,提供更高的并行处理能力。

计算密集型任务:计算密集型任务是指那些主要依赖 CPU 运算能力的任务。通常涉及大量的数值计算、复杂的算法或模型运算,而对于 I/O 操作的需求相对较低,受限于 CPU 的运算速度。
IO密集型任务:I/O 密集型任务是指那些主要依赖于输入/输出操作的任务。这些任务通常需要频繁地进行文件读写、网络通信、数据库访问等 I/O 操作,而对于 CPU 的计算能力需求相对较低。

单核 + 计算密集型任务:

在单核情况下,只有一个物理核心处理器,多线程和单线程最终都落在一个核心处理器(CPU)去处理任务,此时的多线程只是分时段的获取同一个CPU处理任务。就好比工人使用一个计算器来记账,最终都依靠计算器计算加减乘除来获得结果。

在处理计算密集型任务的时候,主要依赖CPU不停的运算来处理任务,这个时候无论是多线程还是单线程,接收单个或多个计算密集型任务,处理效率都没太大差距,因为只有一个CPU在不停地进行运算处理任务,接收的多个任务也只能一个个依次处理。就好比多个工人都在做记账任务,而计算器只有一个,需要等一个工人使用计算完后才能交给下一个工人记账,这个时候一个工人记多笔账还是多个工人分别记,都没太大差别,甚至还差一点,工人之间需要交换计算器会产生耗时,这就是多线程之间的切换开销。

单核+计算密集型任务场景时,使用单线程处理就可以了。

单核 + IO密集型任务:

在处理IO密集型任务的时候,虽然多线程和单线程最终都落在一个核心处理器(CPU)去处理任务,但在处理一个任务的整个过程中,CPU并不需要一直参与运算,此时多线程就可以充分利用CPU。对于单线程,在处理一个任务的整个过程中,只有需要运算的时候CPU才参与运算,其它时候CPU就在空转,一直到线程处理完;对于多线程,一个线程处理任务过程中,在需要运算时使用CPU运算,其它时候就不占用(异步执行IO操作),让其它需要进行运算的线程去占用运算,充分利用CPU让其不停的运算处理。

就好比工人用计算器计算登记货车运来的货物(计算器好比CPU,货车运货物好比IO流),一个工人在等待运砖块的货车,运砖货车一到,工人便拿起计算器累加计算已到的总砖块数量,计算登记好后等待下一辆运转货车到来,计算器也就放在一旁了,而每个工人职责分明,不会去登记其它货物,造成计算器资源浪费。如果同时有多个工人进行登记,一个工人计算登记好运砖货物便释放计算器并等待下一辆运转货车到来;在第一个工人等待过程中,下一个工人可以使用计算器计算登记运沙货物,登记好后释放计算器并等待下一辆运沙货车到来;一直释放、占用循环下去,充分利用计算器资源。

使用多线程能够充分利用CPU,看起来像多个任务同时并行接收与执行,但相对于CPU来说任务还是一个一个依次串行处理,并没有并行执行,只是CPU资源没有浪费,得到充分利用,效率便上来了。

单核+IO密集型任务场景时,使用异步IO操作时可以考虑使用多线程处理能提高处理效率,在进行异步IO操作时,线程可以释放CPU;使用同步IO操作时,线程会被阻塞,不会释放CPU,即使启动多个线程执行,多个线程也只是同时接收任务,最终任务还是被串行执行,所以使用多线程相比于单线程处理效率并没有提高,可能还会效率还低一点。

多核 + 计算密集型任务:

在多核情况下,有多个物理核心处理器,多个线程可以分别获取不同的核心处理器(CPU)同时处理任务。就好比工人使用计算器来记账,可以为每个工人都分配一个计算器,多个工人同时使用计算器计算加减乘除来获得结果。

在处理计算密集型任务的时候,使用多个线程可以同时接收多个计算密集型任务并进行运算处理,多个任务并行运算处理,提高整体的执行效率;而如果只使用单线程来处理,任务只能串行处理,一个线程只能使用一个CPU,其它CPU就处于空闲状态。就好比多个工人都在做记账任务,每个工人都配备一个计算器,这个时候每个工人可以并行计算记账,而无需等待。

多核 + 计算密集型任务场景时,使用多线程处理就可以极大提升任务处理效率。

多核 + IO密集型任务:

在处理IO密集型任务的时候,虽然多线程可以分别落在不同的核心处理器(CPU)去处理任务,不管是同步IO还是异步IO操作,多线程下都可以并行执行多个任务,相比于单线程处理效率得到极大提升。对于同步IO和异步IO操作,异步IO操作下可以使更多线程抢占CPU执行,更充分的利用CPU;同步IO操作会浪费大量的CPU资源。

同样使用工人用计算器计算登记货车运来的货物(计算器好比CPU,货车运货物好比IO流)的例子。对于同步IO,就好比工人使用完计算器后不给下一个工人使用,一直占有知道所有货物运完计算登记完为止。那么一个工人使用一个计算器只登记自己负责的货物线路,多个工人使用多个计算器可以同时并行登记各自负责的货物线路,任务处理速度是上来了,但浪费了更多的资源。对于异步IO,就好比工人使用完计算器后就释放出来不占用,让下一个工人使用。在指定数量的计算器下,就可以有指定数量的工人同时并行计算登记各自的货物线路,在使用计算器计算完后,释放计算器,在等待下一辆货车运货到来之前,就可以有多个计算器释放出来让其它多个工人继续使用登记,宏观上看,在指定数量的计算器下,有更多的工人同时计算登记,这样不仅任务处理速度是上来了,而且计算器也能够得到充分使用。

多核 + IO密集型任务场景时,使用多线程处理就可以极大提升任务处理速度。


异步 I/O 和同步 I/O 是两种不同的 I/O 操作模式,它们在执行过程和效率上有着明显的差异。

同步 I/O 操作:

  • 同步 I/O 操作会阻塞当前线程,直到 I/O 操作完成。
  • 在 I/O操作执行期间,当前线程无法处理其他任务,系统的并发能力受限。
  • 编程模型相对简单,代码结构清晰。
  • 适用于 CPU 密集型任务或 I/O操作时间较短的场景。

异步 I/O 操作:

  • 异步 I/O 操作不会阻塞当前线程,而是继续执行其他任务。
  • I/O 操作完成时,通过回调函数或事件通知调用者。
  • 可以通过多线程同时执行多个异步 I/O 操作,充分利用 CPU 资源,提高 I/O 吞吐量。
  • 适用于 I/O 密集型任务,如网络通信、文件I/O 等,可以提高系统的并发能力和响应性。
  • 但是实现异步 I/O 操作需要更复杂的编程模型,增加了代码的复杂度。

在单核 CPU 情况下,I/O 密集型任务使用同步 I/O 操作时,单线程和多线程的效率对比如下:

单线程同步 I/O 操作:

  • 在单核 CPU 上,当执行同步 I/O 操作时,当前线程会被阻塞,无法执行其他任务。
  • 整个系统只能顺序地执行 I/O 操作,效率较低。
  • 当 I/O 操作时间较长时,系统的响应性会大大降低。

多线程同步 I/O 操作:

  • 在单核 CPU 上,即使启动多个线程执行同步 I/O 操作,也只能被串行执行。
  • 每个线程在执行 I/O 操作时仍然会被阻塞,无法处理其他任务。
  • 线程上下文切换也会带来一定的性能开销。
  • 因此,多线程并不能提高 I/O 密集型任务在单核 CPU上的效率。
  • 20
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值