本书简介
简介一般是一本书的一个概括,本书的核心是 Java 并发 ,那么简介中的内容肯定是与并发相关的知识。
读书之前,如果你对并发一无所知,那么一定要带着问题来读书,首先,最直接的问题:
- 什么是并发?
- 并发是怎么来的?
- 并发带来了什么样的问题?
- 不同的问题是由于什么底层的原因导致的?
刨根问底的弄明白这几个问题,基本上也就对于 并发 有了一个基本而全面的认识。
这里就以问题为线索,以解决问题为目的,来读这一章。
1. 什么是并发?
首先:计算机最早是没有 操作系统 的,每个计算机从头到尾只执行一个程序,这个程序获取所有的资源。
这个年代估计已经很早了,但是想想都可以知道这样是很低效的,并且当时的计算机属于很昂贵的机器,这就更加降低了使用效率。
于是操作系统出现了,有了操作系统之后计算机可以运行多个应用程序了,并且操作系统充当了调度分配者的身份,负责分配计算机资源。
每个程序都是一个独立的进程,每个进程有自己的数据内存空间。于是计算机从单应用时代进入了操作系统出现后的多进程时代。
在这里,本书的简介给出了 操作系统出现的原因:
- 提升公平性: 也就是多个应用程序使用时间片的方式进行切换,而不是没有操作系统时从头到尾运行一个应用程序
- 提升资源利用率: 当程序需要等待外部操作(比如网络或者磁盘I/O)时,可以切换到另一个程序继续运行,就提升了计算机的利用率
- 便利性: 编写多个程序进行计算,在必要的时互相通信,比编写一个巨大的程序来计算所有任务要简单。(有点分布式与巨型单体应用的感觉)
所以为了 性能 与 利用率 , 操作系统出现了。 但是进程还是有点重,因为一个应用程序可能需要很多资源,所以这两个指标继续促进对 进程 进行细分,于是在进程中我们可以同时进行多个控制流,线程 出现了。
比如下面示意图,一个应用程序中有许多个流程并行执行,你在收发群消息的时候,也可以收发好友消息,有时候还会后台给你推送你所在地的新闻,这背后就是多线程 技术的应用。
线程 是轻量级的进程,在「现代操作系统」 中,以线程为基本调度单位,如果没有使用明确的协同机制(比如 Java 中的锁)那么线程将彼此独立执行,所以在多线程环境下会出现许多单线程中没有的问题。
【下面是一个简单的帮助记忆的流程图:】
2. 多线程的优点
这里本书介绍了引入多线程之后的优点:
-
降低开发和维护成本,提升复杂应用程序的性能。
但是其实多线程程序并不好维护,因为其异步的特性,不像同步程序所有流程都写在一起,所以这里的降低可能是与以前的更为复杂的程序做对比。 -
提升图形用户界面的灵敏度
这点毫无疑问,如果没有多线程,那么图形界面将充满了阻塞。 -
在服务器应用程序中,提升资源利用率和系统吞吐率。
Web 服务器是天然的高并发场景,一个服务器同时需要接受很多个客户端的同时访问,如果没有多线程则响应时间会大大提升,甚至不可用。 -
提升多核处理器的利用率
随着时代的发展,现在的 CPU 核心是越来越多了,而线程是可以真正并行在不同核心上的,而且就算在单核 CPU 上,多线程在某些场景也可以提升 CPU 的利用率,比如一个外部 I/O 需要等待回应,这个时间就可以切换到其他线程执行任务,提高 CPU 的利用率 -
多线程程序建模比复杂的异步程序更加简单
还是那句话,简单与复杂是对比出来的。如果一个程序中的任务类型都相同,那么比包含多种异构任务的程序要更加易于编写,测试,并且错误会更少。
作者提出使用线程可以将复杂的异步工作流拆解为「一组」简单的同步工作流,每个流程在单独的线程中运行,并在特定的位置进行交互。
Servlet 和 RMI 框架可以帮助我们实现上述目标,我们在编写 Servlet 程序的时候并不需要了解同时处理多少个请求的具体底层逻辑,以及 Socket 输入流输出流是否被阻塞。我们使用同步的方式来处理请求,就像编写普通的单线程应用那样。
3. 多线程带来的问题
技术的发展总是伴随着问题的出现,为了提高计算机的利用率和最大化性能,操作系统出现了,此时进入多进程时代。
进程过于庞大,于是细分为线程,进一步的细分意味着对资源的更细粒度的掌控,于是 性能和利用率进一步提示,进入多线程时代。
但是多线程环境下也伴随着其特有的问题:
- 可见性问题:CPU 缓存导致 —— 硬件层面
- 有序性问题:编译器优化导致 —— 语言层面
- 原子性问题:操作系统线程切换 —— 操作系统层面
这三个问题都属于安全性问题。
多线程还存在着活跃性和性能问题:
- 当程序无法执行某行代码的时候,就出现了活跃性问题,最典型的就是死锁,多个线程的等待资源被对方持有,导致资源成功获取永远不会发生,进而处于无限的等待状态。
- 线程的切换存在不小的开销,操作系统需要保存当前线程的执行状态和上下文,当存在大量的线程时,反而可能导致应用性能的下降。
所以这就是回答了第一个问题,什么是并发 :并发就是在操作系统中同时运行多个计算。
那么并发是怎样来的? 很明显,并发随着操作系统的出现而来,其目的就是为了提醒:「性能」 和 「利用率」。 这很容易理解,毕竟计算机是资源,我们希望最大化的榨取它的性能,让它一直能处于计算状态有所产出,之前一台计算机智能运行一个程序,多个程序就需要多个计算机,有了操作系统之后计算机可以同时运行多个程序(当然这里在多核心CPU出现之前,这里的同时也只是假象,其真正原因是 CPU 的高频率切换以及操作系统的 「时分复用」 机制)
单线程程序对比多线程程序的优势:
Java 中的并发
上面已经说了,并发的出现是因为操作系统的出现,那么 Java 作为编程语言,也是对并发这种特性有支持的,下面就聊聊 Java 中的并发。
每启动一个 main 方法, 操作系统中就会增加一个 JVM 实例,这个实例中可以启动多个线程,操作系统具体调度的就是 JVM 中的不同线程。
Java 内存模型
Java 中的内存模型设计如下(来自深入学习JVM):
可以看到,每个线程中有自己独显的数据区域,也有线程共享的数据区域。
Java 与并发相关的类:
最直接的类
- Thread , 这个类就是线程对应的抽象对象。
- Object 中也有与线程相对于的方法。
- JUC 并发包中存在着线程池相关的类
Java 中与并发相关的关键字:
- synchronized ,我想就算你没接触过并发,应该也知道 synchronized 锁这个关键字
- volatile ,内存可见性相关的关键字。
Java 中与并发相关的应用
- 最直接的 Web服务器,比如 Tomcat,一个服务器要接受无数的请求,这些请求是被并发执行的,否则的话只有执行完上一个请求才能执行下一个,那么肯定无法满足用户的需求,Web 服务器应用场景是天然的并发场景
- Android 开发,之所以没说 Swing ,是因为太古老了,我最早也接触过一些安卓开发的相关工作,当时刚入门,就接触了线程相关的概念,因为当时渲染界面只能在主线程完成,工作事件要分配的专门的工作线程,不同给的线程负责不同的工作,因为界面的点击也是并发的,可能用户点了一个按钮之后又点了另外的按钮,这些对应着不同的事件和策略。
本章内容脑图
Q.E.D.
Comments | 0 条评论