介绍##
1.1 并发的简短历史
相同的关注点(资源利用,公平和方便) 不仅促进了进程的发展,也促进了线程的发展、
线程允许程序控制流的多重分支同时存在于一个进程。它们共享进程范围内的资源,比如内存和文件句柄,但是线程有自己的程序计数器、栈、和本地变量。
1.2 线程的优点
提高性能,降低程序复杂度。在服务器应用中,提高资源利用率和吞吐量。
1.3 线程的风险
1.3.1 安全危险
竞争条件:当被多线程调用时,getNext是否能返回不重复的值。
因为线程共享相同的内存地址空间,且并发地运行,它可能访问或修改其他线程正在使用的变量。
1.3 线程无处不在
定时器:Timer访问的数据必须是线程安全的。
Servlet:Servlet可能会访问共享的对象(Session等),必须是线程安全
远程方法调用:RMI使你能够调用在另外一个JVM上运行的对象的方法。
线程安全##
编写线程安全的代码,本质上就是对状态的方法,而且通常都是共享的、可变的状态。
状态:一个对象的状态就是它的数据,存储在状态变量,比如实例或静态域。
包含了任何会对它外部可见行为产生影响的数据。
共享:指一个变量可以被多个线程访问。
可变:指变量在其生命周期内可以改变。
2.1 什么是线程安全性
2.1.1 无状态的servlet
无状态对象永远是线程安全的。
不包含域,也没有引用其他类的域
2.2 原子性
原子性:不能作为单独的、不可分割的操作。
count++:包含了三次操作,读-改-写。
2.2.1 竞争条件
检查再运行:使用潜在的过期观察值来做决策或执行计算。
2.2.2 惰性初始化中的竞争条件
检查再运行的常见用法是惰性初始化:
竞争条件并不总是失败的,还需要某些特殊的分时。
2.2.3 复合操作
检查再运行和读-改-写的操作看成复合操作:操作必须原子地执行。
可以使用已有的线程安全类:aromic包。
2.3 锁
为了保护状态的一致性,要在单一的原子操作中更新相互关联的状态变量。
2.3.1 内部锁
内部锁:每个Java对象都可以隐式地扮演一个用于同步的锁的角色。
synchronized放的锁就是该方法所在的对象本身。
2.3.2 重进入
当一个线程请求其他线程已经占有的锁时,请求线程将被阻塞。
然后内部锁是可重进入的。
JVM会被每个锁关联一个请求计数,计数为0表示未被占有。
子类覆写了父类的synchronized类型的方法,如果没有可重入锁,看上去像死锁。
2.4 用锁来保护状态
2.5 活跃度和性能
service方法声明为synchronized,因此每次只能有一个线程执行它。这违背了Servlet的初衷:同时处理多个请求。