Java 并发是一个术语,涵盖了 Java 平台上的多线程、并发和并行。其中包括 Java 并发工具、问题和解决方案。本教程涵盖了多线程的核心概念、并发构造、并发问题、代价以及与 Java 中多线程相关的好处。

什么是多线程?

多线程意味着在同一个应用程序内有多个执行线程。一个线程就像一个执行应用程序的独立 CPU。因此,多线程应用程序就像有多个 CPU 同时执行代码的不同部分。

1. Java 并发和多线程教程 - 图1

不过,一个线程并不等于一个 CPU。通常,一个 CPU 会在多个线程之间共享其执行时间,在执行每个线程一段时间之间切换。应用程序的线程也可以由不同的 CPU 执行。

1. Java 并发和多线程教程 - 图2

为什么要多线程?

在应用程序中使用多线程有几个原因。最常见的原因为:

  • 更好地利用单个 CPU。
  • 更多地利用多个 CPU 或者 CPU 内核。
  • 与响应性有关的更好的用户体验。
  • 与公平有关的更好的用户体验。

在后续小节我们会更详细解释这些原因。

更好地利用单个 CPU

最常见的原因之一就是能更好地利用计算机中的资源。比如,如果一个线程正在等待网络发送回请求的响应,那么另一个线程就可以同时使用 CPU 做其它事情。此外,如果计算机有多个 CPU,或者 CPU 有多个执行核,那么多线程也可以帮助应用程序利用这些额外的 CPU 核。

更多地利用多个 CPU 或者 CPU 内核

如果计算机包含多个 CPU,或者 CPU 包含多个执行核,那么对应用程序需要使用多线程才能使用所有 CPU 或者 CPU 核。一个线程最多能使用 一个 CPU,并且如上所述,有时候甚至不能完全使用一个 CPU。

与响应性有关的更好的用户体验

使用多线程的另一个原因是提供更好的用户体验。比如,如果点击 GUI 上的一个按钮,而这导致请求通过网络发送出去,那么哪个线程执行这个请求就很重要了。如果用同一个线程还更新 GUI,那么 GUI 线程等待请求的响应时,用户可能就会体验到 GUI 挂起。相反,这样的请求可以由后台线程执行,这样 GUI 线程就可以同时自由地响应其他用户请求。

与公平有关的更好的用户体验

第四个原因是在用户之间更公平地共享计算机资源。比如,假设一台服务器接受客户端的请求,并且只有一个线程执行这些请求。如果一个客户端发送一个需要很长时间才能处理完的请求,那么所有其他客户端的请求就必须等待该请求完成。通过让每个客户端的请求由它自己的线程来执行,那么就没有一个任务能完全独占 CPU。

多线程与多任务

在过去,计算机只有一个 CPU,一次只能执行一个程序。大多数小型计算机都不足以同时执行多个程序,因此没有尝试过这种方法。公平地说,许多大型机系统能够一次执行多个程序的时间比个人计算机长得多。

多任务

后来出现了多任务,这意味着计算机可以同时执行多个程序(即任务或者进程)。不过,这不是真正的“同时”。一个 CPU 在程序之间共享。操作系统会在运行的程序之间切换,在切换之前执行每个程序一段时间。

随着多任务的出现,软件开发人员面临新的挑战。程序不能再假定所有的 CPU 时间可用,也不能假定所有的内容或者所有其他计算机资源可以用。一个好公民程序应该释放所有它不再使用的资源,这样其他程序就能用这些资源。

多线程

后来出现了多线程,也就是说在同一个程序内可以有多个执行线程。一个执行线程可以看作是执行程序的一个 CPU。当有多个线程执行同一个程序时,就好像在同一个程序内有多个 CPU 执行一样。

多线程很难

多下才能哼是提升某些类型的程序的性能的好方法。不过,多线程甚至比多任务更具挑战性。线程是在同一个程序中执行的,因此会同时读、写同一内存。这会导致在单线程程序中看不到的错误。有些错误可能在一个 CPU 的机器上看不到,因为两个线程从来不会真正同时执行。不过,现代计算机带有多核 CPU,甚至还有多个 CPU。这意味着不同的线程可以在不同的核或者 CPU 上同时执行。

1. Java 并发和多线程教程 - 图3

如果一个线程读一个内存位置,而另一个线程向其写入,那么第一个线程最终会读取什么值?旧的值?被第二个线程写的值?或者一个介于二者之间的值?或者,如果两个线程同时向同一个内存位置写数据,那么完成时,哪个值会留下来?第一个线程写的值,还是第二个线程写的值,或者两个值的混合?

如果没有适当的预防措施,任何这些结果都是可能的。这种行为甚至是不可预测的。结果随时可能会改变。因此,作为一个开发人员,了解如何采取正确的预防措施是很重要的,这意味着要学习如何控制线程访问共享资源(内存、文件、数据库等)。这也是本教程要解决的主题之一。

Java 中的多线程和并发

Java 是最早让开发人员可以轻松使用多线程的语言之一。它从一开始就有多线程的能力。因此,Java 开发人员经常会面临上述问题。这就是我们写这篇有关 Java 并发的文章的原因。

本文主要关注 Java 中的多线程,不过多线程中出现的一些问题与多任务和分布式系统中出现的问题类似。因此,对多任务和分布式系统的引用也出现在本文中。因此,我们用并发这个词,而不是多线程这个词。

并发模型

第一个 Java 并发模型假设在同一应用程序内执行的多个线程也会共享对象。这种类型的并发模型通常称为共享状态并发模型。很多并发语言构造和实用程序都是为支持这种并发模型而设计的。

不过,从第一本 Java 并发编程书籍问世以来,甚至 Java 5 并发实用程序发布依赖,并发架构和设计领域已经发生了很多事情。

共享状态并发模型导致了很多难以优雅解决的并发问题。因此,一种被称为“无共享”或者“分离状态”的可选并发模型已广受欢迎。在分离状态并发模型中,线程不会共享任何对象或者数据。这就避免了共享状态并发模型的很多并发访问问题。

新的异步“分离状态”平台和工具包,比如 Netty、Vert.x 和 Play /Akka 和 Qbit,已经出现。新的非阻塞并发算法已经发布,像 LMax Disrupter 这样的非阻塞工具已经添加到工具包中。新的函数式编程并行性已经引入,比如 Java 7 中的 Fork 和 Join 框架,以及 Java 8 中的集合流 API。

有了所有这些新的发展,是时候更新这个 Java 并发教程了。因此,本教程是正在进行中的。只有有时间,新的教程就会发布。

Java 并发学习指南

如果是 Java 并发新手,推荐遵循下面的学习计划。

通用并发和多线程理论:

Java 并发编程的基础知识:

Java 并发编程中的典型问题:

帮助解决上述问题的 Java 并发构造:

Java 并发实用程序 (java.util.concurrent):

进一步的主题:

原文地址:http://tutorials.jenkov.com/java-concurrency/