多线程
介绍
并发历史
推动多程序同时执行的发展
- 资源利用
- 公平
- 方便
相同的关注点不仅促进了进程的发展,也促进了线程的发展。
每个线程(现代操作系统把进程看作为时序调度的基本单元)包括
- 程序计数器
- 栈
- 本地变量
线程优点
- 降低开发和维护开销,提高复杂应用性能
- 把异步工作流程转化为普遍存在的顺序流程
- 改进用户接口响应性
- 提高资源的利用率和吞吐量
- 简化JVM实现,GC通常运行于一个或多个持续工作的线程之间
- 使用多处理器
- 模型的简化
- 对异步事件的简单处理
- 用户界面的更加响应性
线程的风险
线程安全性问题
- 安全风险
@NotThreadSafe
public class UnsafeSequence {
private int value;
public int getNext(){
return value++;
}
}
在一些特殊时序情况下,两个线程可调用getNext并得到相同的返回值。
++操作分为三个独立操作:
- 读取这个值
- 使之加1
- 再写入新值
多个线程中,可能交替占有运行时,所以两个线程可能同时读取同一个值,并都使之加1。
这是一种常见的并发危险:竞争条件。
Java同步机制,协调多线程下对共享变量访问。
@ThreadSafe
public class UnsafeSequence {
@GuardedBy("this") private int value;
public synchronized int getNext(){
return value++;
}
}
- 活跃度风险
线程的使用引入了不会出现在单线程化程序中的额外安全危险。
线程的使用引入了又一形势的活跃度失败,这不会出现在单线程化的程序中。
当一个活动进入某种它永远无法再继续执行的状态时,活跃度失败就发生了。比如:死循环
多线程引入带来更多的活跃度风险:
- A线程等待B线程独立占有资源,B线程不释放,A线程将永久等待。
- 性能风险
- 服务时间
- 响应性
- 吞吐量
- 资源消费
- 可伸缩性的不良表现
- 线程的上下文切换,保存和恢复线程执行上下文,非常消耗CPU的时间在对线程的调度,而不是运行。
线程共享数据,必须使用同步机制,会限制编译器优化,能够清空或锁定内存和高速缓存,并在共享内存的总线上创建同步通信。
线程无处不在
-
每个Java应用程序都使用线程。JVM启动后,他创建一些线程来进行自身的常规管理(垃圾回收,终结处理),以及一个运行main函数的主线程。
-
线程安全需要的并不仅仅在于框架调用的组件,只要它处于组件访问过的程序状态段,他就会扩展到所有代码路径。因此,线程安全的需要是具有传递性的。
通过从框架线程中调用应用程序的组件,框架把并发引入了应用程序。组件总是需要访问程序的状态,因此需要在所有代码路径访问状态时,必须是线程安全的。
-
引发非应用程序管理线程调用应用程序代码这种情况。
- 定时器
- JSPs (Servlets and JavaServer Pages)
- 远程方法调用(Remote Method Invocation)
- Swing 和 AWT