1:Program order rule. Each action in a thread happens-before every action in that thread that comes later in the program order. 同一个线程中,书写在前面的操作happen-before书写在后面的操作。 这个原则是在单线程里面的 。 假设 有
int x,y;
x=1; //a
y=2 //b 那么 a happens-before b; happens-before 是这样定义的,如果B能够看到A动作产生的结果,我们说A happens-before B 。
还有 java内存模型里面 是有主存和工作内存 那么问题是 这里面 a happens-before b,是指在 工作内存中可以看到 ,还是在主存里面也可以 。 简单来说 ,就是 工作内存 的 y=2 会不会先于x=1 存到主存里面。 比如另外一个线程 在读到 y=2的时候 ,是不是一定 会读到 x=1 。还是 他读到y=2的时候 ,x有可能是0
int x,y;
x=1; //a
y=2 //b 那么 a happens-before b; happens-before 是这样定义的,如果B能够看到A动作产生的结果,我们说A happens-before B 。
还有 java内存模型里面 是有主存和工作内存 那么问题是 这里面 a happens-before b,是指在 工作内存中可以看到 ,还是在主存里面也可以 。 简单来说 ,就是 工作内存 的 y=2 会不会先于x=1 存到主存里面。 比如另外一个线程 在读到 y=2的时候 ,是不是一定 会读到 x=1 。还是 他读到y=2的时候 ,x有可能是0
如何避免这种情况?排序原则已经做到:
a, 在程序顺序中, 线程中的每一个操作, 发生在当前操作后面将要出现的每一个操作之前.
b, 对象监视器的解锁发生在等待获取对象锁的线程之前.
c, 对volitile关键字修饰的变量写入操作, 发生在对该变量的读取之前.
d, 对一个线程的 Thread.start() 调用 发生在启动的线程中的所有操作之前.
e, 线程中的所有操作 发生在从这个线程的 Thread.join()成功返回的所有其他线程之前. 为了实现 happends-before ordering原则, java及jdk提供的工具:
a, synchronized关键字
b, volatile关键字
c, final变量
d, java.util.concurrent.locks包(since jdk 1.5)
e, java.util.concurrent.atmoic包(since jdk 1.5)
…使用了happens-before ordering的例子:
(1) 获取对象监视器的锁(lock)(2) 清空工作内存数据, 从主存复制变量到当前工作内存, 即同步数据 (read and load)(3) 执行代码,改变共享变量值 (use and assign)(4) 将工作内存数据刷回主存 (store and write)(5) 释放对象监视器的锁 (unlock)注意: 其中4,5两步是同时进行的.这边最核心的就是第二步, 他同步了主内存,即前一个线程对变量改动的结果,可以被当前线程获知!(利用了happens-before ordering原则)对比之前的例子
如果多个线程同时执行一段未经锁保护的代码段,很有可能某条线程已经改动了变量的值,但是其他线程却无法看到这个改动,依然在旧的变量值上进行运算,最终导致不可预料的运算结果。
private Resource resource = null;
public Resource getResource() {
if (resource == null) {
synchronized {
if (resource == null)
resource = new Resource();
}
}
return resource;
}
}
他的解释是上面的例子不会出现dcl问题。而下面一个例子
public MyObject{
private static MyObect obj;
private Date d = new Data();
public Data getD(){return this.d;}
public static MyObect getInstance(){
if(obj == null){
synchronized(MyObect .class){
if(obj == null)
obj = new MyObject();//这里
}
}
return obj;
}
}
会出现dcl问题 。因为 如果 线程a运行到“这里”,而线程b,并且调用b。getD();这个时候 可能会有问题,对于"这里"这条语句,线程A还没有离开同步块.
因为没有"离开同步块"这个条件,线程a的工作区没有强制与主存储器同步,这时工作区中有两个字段
obj,d 到底先把谁同步到主存储区,没有条件限制,虽然在线程A的工作区obj和d都是完整的,但有JSL
没有强制不允许先把obj映射到主存储区,如果哪个jvm实现按它的优化方案先把工作存储器中的obj
同步到主存储器了,这时正好线程B获取了,而d却没有同步过去,那么线程B就获取了obj的引用却找不能obj.getD();我的问题是 按照java的加载类的顺序 是先 按照顺序初始化 语句 ,初始化块 ,然后 执行构造函数
那么 Date d = new Data() happens-before new MyObject()执行构造函数 满足单线程的happens-before原则这个时候线程b 既然能看到 new MyObject()的值 ,为什么 看不到 Date d = new Data() 的结果是不是 单线程的 happens-before原则 仅仅是在 工作内存中
请高人指点