打开计算机电源后,启动的程序是操作系统(OS)。操作系统控制机器硬件和可以在机器上运行的程序。当你启动一个程序时,操作系统会创建一个新的进程。这意味着操作系统在一个表(数组)中分配一个新的条目,在这个表(数组)中它管理进程,并填充它知道的和需要知道的有关进程的参数。例如,它注册允许进程使用的内存段、进程的 ID、启动它的用户以及启动它的其他进程。你不能凭空开始一个过程。当你双击一个 EXE 文件时,你实际上告诉文件管理器(一个作为进程运行的程序)把 EXE 文件作为一个单独的进程启动。浏览器通过一个 API 调用系统,并请求操作系统这样做。操作系统将把 explorer 进程注册为新进程的父进程。此时操作系统实际上并不启动进程,而是创建它随后启动进程所需的所有数据,当有一些空闲的 CPU 资源时,进程启动,然后很快暂停,重新启动,然后暂停,依此类推。您不会注意到它,因为操作系统会一次又一次地启动它,并且总是反复暂停进程。它需要这样做才能为所有进程提供运行的可能性。这样,我们可以体验到所有进程同时运行。实际上,进程不会在单个处理器上同时运行,但它们经常会有时间段运行,因此我们感觉它们一直在运行。
打开计算机电源后,启动的程序是操作系统(OS)。操作系统控制机器硬件和可以在机器上运行的程序。当你启动一个程序时,操作系统会创建一个新的进程。这意味着操作系统在一个表(数组)中分配一个新的条目,在这个表(数组)中它管理进程,并填充它知道的和需要知道的有关进程的参数。例如,它注册允许进程使用的内存段、进程的 ID、启动它的用户以及启动它的其他进程。你不能凭空开始一个过程。当你双击一个 EXE 文件时,你实际上告诉文件管理器(一个作为进程运行的程序)把 EXE 文件作为一个单独的进程启动。浏览器通过一个 API 调用系统,并请求操作系统这样做。操作系统将把资源管理器进程注册为新进程的父进程。此时操作系统实际上并不启动进程,而是创建它随后启动进程所需的所有数据,当有一些空闲的 CPU 资源时,进程启动,然后很快暂停,重新启动,然后暂停,依此类推。您不会注意到它,因为操作系统会一次又一次地启动它,并且总是反复暂停进程。它需要这样做才能为所有进程提供运行的可能性。这样,我们可以体验到所有进程同时运行。实际上,进程不会在单个处理器上同时运行,但它们经常会有时间段运行,因此我们感觉它们一直在运行。
如果计算机中有多个 CPU,那么进程实际上可以与有 CPU 的多个进程同时运行。随着集成的日益高级,台式计算机拥有包含多个核心的 CPU,它们几乎与单独的 CPU 一样运行。在我的机器上,我有四个内核,每个内核都能同时执行两个线程;所以,我的 MacOS 几乎就像一台 8 CPU 机器。当我开始工作时,一台 8 CPU 的电脑是一台价值百万美元的机器。
如果在不同的线程中执行是异步的,或者在调用 run 方法的同一个线程中执行,那么这是一个需要分离的不同关注点。如果这样做的话,我们可以将类作为构造器参数传递给一个`Thread`对象。对`Thread`对象调用`start`将启动我们传递的对象的 run 方法。这不是收益。好处是我们还可以将`Runnable`对象传递给`Executor`(可怕的名字,哈!)。`Executor`是一个接口,实现以高效的方式在`Thread`对象中执行`Runnable`(还有`Callable`,见下文)对象。执行者通常有一个准备就绪并处于`BLOCKED`状态的`Thread`对象池。当`Executor`有一个新任务要执行时,它将它交给`Thread`对象之一,并释放阻塞线程的锁。`Thread`进入`RUNNABLE`状态,执行`Runnable`,再次被阻塞。它不会终止,因此,可以在以后重用它来执行另一个`Runnable`。这样,`Executor`实现就避免了操作系统中线程注册的资源消耗过程。
它说启动由`supply_value`表示的`Supplier`,当它完成时,将这个值提供给由`consume_the_value`表示的消费者。示例代码计算 pi 的值并提供该值。`consume_the_value`部分将值打印到输出。当我们运行代码时,文本`All is scheduled`可能会首先打印到输出中,然后才打印 pi 的计算值。
它说启动由`supply_value`表示的`Supplier`,当它完成时,将这个值提供给由`consume_the_value`表示的消费者。示例代码计算 PI 的值并提供该值。`consume_the_value`部分将值打印到输出。当我们运行代码时,文本`All is scheduled`可能会首先打印到输出中,然后才打印 PI 的计算值。
如果我删除 synchronized 块,那么我用来测试 Java HotSpot(TM)64 位服务器 VM 的 JVM(build 9-ea+121,mixed mode 和 18.3 build 10+46,mixed mode for the second edition of the book)打印出失败,try count 大约为几百次。(看看 Packt 提供的代码库中的`SynchronizedDemoFailing`类。)
现实生活中的问题往往规模不理想。我们已经访问了一个扩展性很好的示例,尽管它不是理想的快速排序。这一次,我们将为更接近现实问题的问题开发一个并行算法。在 N 个处理器上解算智囊团游戏不会使解算速度提高`N`倍,而且代码也不平凡。这个例子将向您展示现实生活中的问题是什么样子的,尽管它不会教您所有可能的问题,但是当您在商业环境中第一次看到其中一个问题时,您不会感到震惊。