1.1 并发与并行的关系
可以说,并发很好地利用了CPU时间片的特性,也就是操作系统选择并运行一个任务,接着在下一个时间片内运行另一个任务,并把前一个任务设置成等待状态。其实并发并不意味着并行。
具体列举下面几种情况。
◎ 有时候,多线程执行会提高应用程序的性能,而有时候反而会降低应用程序的性能。这在JDK中Stream API的使用上体现得很明显,如果任务量很小,而我们又使用了并行流,反而降低了应用程序的性能。
◎ 在多线程编程中,可能会同时开启或者关闭多个线程,这样会产生很大的性能开销,也降低了应用程序的性能。
◎ 当线程同时处于等待I/O的过程中时,并发可能会阻塞CPU资源,其后果不仅是用户长时间等待,而且会浪费CPU的计算资源。
◎ 如果几个线程共享了一个数据,情况就会变得有些复杂。我们需要考虑数据在各个线程中的状态是否一致。为了达到数据一致的目的,很可能会使用synchronized或者lock相关操作。
现在,你对并发有一定的了解了吧。并发很好,但并不一定会实现并行。并行是在多核CPU上同一时间运行多个任务或者一个任务分为多块同时执行(如ForkJoin)。单核CPU的话,就不要考虑并行了。
补充一点,实际上多线程就意味着并发,但是并行只发生在这些线程在同一时间调度、分配到不同CPU上执行的情况下。也就是说,并行是并发的一种特定形式。一个任务里往往会产生很多元素,这些元素在不参与操作的情况下大都只能处于当前线程中,这时我们可以对其进行ForkJoin,但这对很多程序员来讲有时候很不好操作、控制,上手难度有些大。这时如果用响应式编程,就可以简单地通过所提供的调度API轻松做到事件元素的下发、分配,其内部会将每个元素包装成一个任务并提交到线程池中,我们可以根据任务是计算型的还是I/O型的来选择相应的线程池。
在这里,需要强调一下,线程只是一个对象,不要把它想象成CPU中的某一个执行核心,这是很多人都在犯的错,CPU时间片会切换执行这些线程。