要想充分发挥多处理器系统的强大计算能力,最简单的方式就是使用线程。
1.1 并发简史
不包含操作系统
-> 操作系统的出现
- 资源利用率
- 公平性
- 便利性
串行编程模型的优势在于其直观性和简单性
在同一个程序中的多个线程可以被同时调度到多个CPU上运行
1.2 线程的优势
线程能够将大部分的异步工作流转换成串行工作流
- GUI:提高UI响应灵敏度
- 服务器:提升资源利用率和系统吞吐率
1.2.1 发挥多处理器的强大能力
通过提高时钟频率来提升性能已变得越来越困难 -> 在单个芯片上放置多个处理器核
设计正确,多线程程序:
- 可以通过提高处理器资源的利用率来提升系统吞吐率
- 有助于在单处理器系统上获得更高的吞吐率
1.2.2 建模的简单性
线程可以形成一种串行执行的假象
通过使用线程,可以将复杂并且异步的工作流进一步分解为一组简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置进行交互
1.2.3 异步事件的简化处理
nio:非阻塞IO
1.2.4 响应更灵敏的用户界面
传统的GUI应用程序:
- 单线程
- 在代码的各个位置调用poll方法来获得输入事件(poll:返回并移除队列头)
- 或者通过“主事件循环”来间接地执行应用程序的所有代码
现代的GUI框架:
- 采用一个事件分发线程来替代主事件循环
- 事件发生时,在事件线程中将调用应用程序的事件处理器
- 仍然存在主事件循环,但它现在处于GUI工具的控制下并在其自己的线程中运行
1.3 线程带来的风险
1.3.1 安全性问题
安全性:永远不发生糟糕的事情
线程安全是不可破坏的
在没有充足同步的情况下,多个线程的操作执行顺序是不可预测的
竞争条件 -> 同步机制
1.3.2 活跃性问题
活跃性:某件正确的事情最终会发生
各种形式的活跃性问题:
- 死锁
- 饥饿
- 活锁
活跃性问题的错误依赖于不同线程的事件发生时序
1.3.3 性能问题
性能:正确的事情尽快发生
性能问题包括:
- 服务时间过长
- 响应不灵敏
- 吞吐率过低
- 资源消耗过高
- 可伸缩性较低
上下文切换:
- 保存和恢复执行上下文
- 丢失局部性
同步机制:
- 抑制编译器优化
- 使内存缓存区中的数据无效
- 增加共享内存总线的同步流量
1.4 线程无处不在
每个Java应用程序都会使用线程:
- JVM启动时,为JVM内部任务(垃圾收集、终结操作等)创建后台线程
- 创建一个主线程运行main方法
线程安全性需求在程序中的蔓延
- 在某段的代码中引入并发性时,对线程安全性的需求要延伸到需要访问【这些代码所访问的程序状态】的所有代码路径