java 并发编程实战 第一天
第一第二章直接省略..
第三章
先有一个概念
java内存模型规定了所有的变量都存储在主内存中, 除此之外每个线程都有自己的工作内存, 线程的工作内存中保存了被该线程使用到的变量的副本拷贝, 线程对变量的所有操作(读取, 赋值等)都必须在工作内存中进行, 而不能直接读写主内存中的变量. 不同的线程之间也无法直接访问对方工作内存中的变量, 线程间变量值的传递均需要通过主内存来完成.
由上可知, 一个线程修改了变量的值, 另一个线程并非总是能够及时获知最新的值, 这就是可见性问题的根源.(引出可见性的问题.)
volatile 保证了线程能够看到主线程的最新值.(
volatile无法保证操作的原子性, 只能保证变量的可见性
)
1. 更改不依赖于当前值, 或者能够确保只会在单一线程中修改变量的值. 如果对变量的更改依赖于现有值, 就是一个race condition操作, 需要使用其他同步手段如synchronized将race condition操作转换为原子操作, 而volatile对原子性是无能为力的. 但是如果能够确保只会在单一线程中修改变量的值, 那么除了当前线程外, 其他线程不能更改变量的值, 此时race condition就不可能发生.
2. 变量不需要与其他状态变量共同参与不变约束. 比如start和end变量都被声明为volatile, 并且start和end组成不变约束start<end, 这样的不变约束是存在并发问题的:
发布与逸出
发布:
一个对象是指它能够被当前范围之外的代码所使用。
逸出:如果发布对象时,它还没完成构造,同样危及封装性,并使程序难以维持稳定.
public class A {
private boolean isIt;
private String yesItIs;
public A() {
EventListener el = new EventListener() { ....};
StaticListeners.register(el);
//因此你的对象就可能被另一个线程使用,尽管它没有完成初始化。
isIt = true;
yesItIs = "yesItIs";
}
}
不变性
--->
不可变对象一定是线程安全的.
当满足一下条件时,对象才是不可变的:
- 对象创建以后其状态就不能改变.
- 对象的所有域都是final类型
- 对象是正确创建的(在对象的创建时间,this 引用没有逸出) // this引用逸出...不太懂.
//3-13使用指向不可变容器对象的volatile类型引用已缓存最新的结果.
public class volatileCachedFactorizer implements Servlet{
private volatile Object o = new ......;//这里的代码我就不全部抄了
public void service (ServletRequest req ,ServletResponse resp){
BigInteger[] num = o.get(....);//这里类用了volatile 保证了获取的是最新值.
if(num ==null){
......
o = new XXX();//因为前面拿到的就已经是最新的,保证了线程安全,不用担心被new了两次.
}
}
}
安全发布的常用模式:
- 在静态初始化函数中初始化一个对象引用.
public static Holder holder = new Holder(42);
- 将对象的引用保存到volatile类型的域或者AtomReferance对象中.
- 将对象的引用保存到某个正确构造方法对象的final类型域中.
- 将对象的引用保存到一个由锁保护的域中.
在并发程序中使用和共享对象时.
- 线程的封闭,对象只能有一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改.
- 只读共享.共享的只读对象包括不可变对象和事实不可变对象.
- 线程安全共享.
- 保护对象 --->加锁.