Introduction
1.1 并发简史
- 早期计算机不包含操作系统,只能从头到尾执行一个程序,程序访问全部资源.
- 操作系统使得程序在单独的进程里运行.
- 操作系统为进程分配各种独占的资源.
- 进程之间通过粗粒度的通信机制来通信.
- 进程出现的原因:
- 资源利用率: 当一个程序等待外部操作时,可以使得另一个程序运行.
- 公平性: 使用粗粒度时间分片,使得用户平等的使用计算机.
- 便利性: 计算多个任务,采用多个程序要比一个计算计算全部任务要容易实现.
- 每个进程相当于一台虚拟的冯诺依曼计算机.
- 存储指令和数据的内存空间.
- 根据机器语言指令以串行的方式执行.
- 通过I/O指令与外部设备通信.
- 线程允许在同一个进程内存在多个程序控制流.
- 线程共享进程内资源.
- 独立拥有程序计数器,栈以及局部变量.
- 可以同时被调度到多个cpu上运行.
- 线程是基本的调度单位.
- 线程之间共享进程内堆空间,采用比进程间共享数据更细粒度的数据共享机制.
1.2 线程的优势
- 发挥多处理器的强大能力(系统角度)
- 多处理器成为计算机的主流趋势.
- 多线程程序可以同时在多个处理器上执行,提高处理器资源的利用率,从而提高系统吞吐.
- 即使在单处理器系统上,针对I/O密集型程序也能提供系统吞吐.
- 建模的简单性(客户端角度)
- 单一任务的编写要比多任务的编写要简单.
- 每个任务分配到一个线程里,简化每个任务的编写.
- 任务在特定的同步位置进行交互.
- 将请求管理,线程创建,负载平衡等功能交给框架实现,用户只需要当做单线程程序来进行实现.
- 异步事件的简化处理(服务器端角度)
- 单线程应用在处理请求过程中,无法响应其他请求.
- 非阻塞I/O较为复杂
- 每个客户端分配一个线程在某些平台也是可行的.
- 响应更灵敏的用户界面
- 单线程gui框架主要依赖poll方法和主事件循环.
- 现代gui框架采用事件分发线程.长时间运行任务在单独线程执行.
1.3 线程带来的风险
- 安全性问题
- 没有合理同步的情况下,由于线程运行不恰当的顺序导致不可预测的结果,这称之为竞态条件
- 多线程共享内存地址空间,因此会出现并发
- 安全性含义:永远不会发生错误的事情.即正确性.
- 活跃性问题
- 正确的事情一定会发生.
- 死锁
- 饥饿
- 活锁
- 性能问题
- 线程上线文切换带来的开销
- 保存和恢复上下文.
- 局部性丢失.
- cpu时间用于调度而非运行.
- 共享数据采用同步机制会抑制编译器优化,使内存缓冲区无效.
- 线程上线文切换带来的开销
1.4 线程无处不在
框架在应用程序里引入的并发性,并不只局限于框架,因为框架本身会回调应用程序的代码,因此线程安全性会在应用程序中蔓延.
- Timer类
- Servlet
- RMI
- Swing和AWT