本文是由JR主持写作
![](/icons/93126de.gif)
![](/icons/93126smhl.gif)
J2SE进阶
![](/icons/93126smhr.gif)
![](/icons/93126yi.gif)
书
![](/icons/93126de.gif)
部分章节整理而成
![](/icons/93126dou.gif)
![](/icons/93126smhl.gif)
J2SE进阶
![](/icons/93126smhr.gif)
正在写作、完善阶段
![](/icons/93126dou2.gif)
您阅读后
![](/icons/93126dou.gif)
有任何建议、批评
![](/icons/93126dou.gif)
请和我联系
![](/icons/93126dou.gif)
或在这儿留言
![](/icons/93126dou2.gif)
![](/icons/93126smhl.gif)
J2SE进阶
![](/icons/93126smhr.gif)
写作项目组感谢您阅读本文
![](/icons/93126dou2.gif)
Java在语言层次上实现了对线程
![](/icons/93126de.gif)
支持
![](/icons/93126dou2.gif)
它提供了Thread/Runnable/ThreadGroup等
![](/icons/93126yi.gif)
系列封装
![](/icons/93126de.gif)
类和接口
![](/icons/93126dou.gif)
让
![](/icons/93126chengxu.gif)
员可以高效
![](/icons/93126de.gif)
开发Java多线程应用
![](/icons/93126dou2.gif)
为了实现同步
![](/icons/93126dou.gif)
Java提供了synchronize关键字以及object
![](/icons/93126de.gif)
wait
![](/icons/93126kh.gif)
/not
![](/icons/93126if.gif)
y
![](/icons/93126kh.gif)
机制
![](/icons/93126dou.gif)
可是在简单易用
![](/icons/93126de.gif)
背后
![](/icons/93126dou.gif)
应藏着更为复杂
![](/icons/93126de.gif)
玄机
![](/icons/93126dou.gif)
很多问题就是由此而起
![](/icons/93126yi.gif)
、Java内存模型
在了解Java
![](/icons/93126de.gif)
同步秘密的前
![](/icons/93126dou.gif)
先来看看JMM(Java Memory Model)
![](/icons/93126dou2.gif)
Java被设计为跨平台
![](/icons/93126de.gif)
语言
![](/icons/93126dou.gif)
在内存管理上
![](/icons/93126dou.gif)
显然也要有
![](/icons/93126yi.gif)
个统
![](/icons/93126yi.gif)
![](/icons/93126de.gif)
模型
![](/icons/93126dou2.gif)
而且Java语言最大
![](/icons/93126de.gif)
特点就是废除了指针
![](/icons/93126dou.gif)
把
![](/icons/93126chengxu.gif)
员从痛苦中解脱出来
![](/icons/93126dou.gif)
不用再考虑内存使用和管理方面
![](/icons/93126de.gif)
问题
![](/icons/93126dou2.gif)
可惜世事总不尽如人意
![](/icons/93126dou.gif)
虽然JMM设计上方便了
![](/icons/93126chengxu.gif)
员
![](/icons/93126dou.gif)
但是它增加了虚拟机
![](/icons/93126de.gif)
复杂程度
![](/icons/93126dou.gif)
而且还导致某些编程窍门技巧在Java语言中失效
![](/icons/93126dou2.gif)
JMM主要是为了规定了线程和内存的间
![](/icons/93126de.gif)
![](/icons/93126yi.gif)
些关系
![](/icons/93126dou2.gif)
对Java
![](/icons/93126chengxu.gif)
员来说只需负责用synchronized同步关键字
![](/icons/93126dou.gif)
其它诸如和线程/内存的间进行数据交换/同步等繁琐工作均由虚拟机负责完成
![](/icons/93126dou2.gif)
如图1所示:根据JMM
![](/icons/93126de.gif)
设计
![](/icons/93126dou.gif)
系统存在
![](/icons/93126yi.gif)
个主内存(Main Memory)
![](/icons/93126dou.gif)
Java中所有变量都储存在主存中
![](/icons/93126dou.gif)
对于所有线程都是共享
![](/icons/93126de.gif)
![](/icons/93126dou2.gif)
每条线程都有自己
![](/icons/93126de.gif)
工作内存(Working Memory)
![](/icons/93126dou.gif)
工作内存中保存
![](/icons/93126de.gif)
是主存中某些变量
![](/icons/93126de.gif)
拷贝
![](/icons/93126dou.gif)
线程对所有变量
![](/icons/93126de.gif)
操作都是在工作内存中进行
![](/icons/93126dou.gif)
线程的间无法相互直接访问
![](/icons/93126dou.gif)
变量传递均需要通过主存完成
![](http://www.crazycoder.cn/WebFiles/200812/c2b27ef4-4209-4e24-8da6-14264806f441.g<img)
>
图1 Java内存模型举例图
线程若要对某变量进行操作
![](/icons/93126dou.gif)
必须经过
![](/icons/93126yi.gif)
系列步骤:首先从主存复制/刷新数据到工作内存
![](/icons/93126dou.gif)
然后执行代码
![](/icons/93126dou.gif)
进行引用/赋值操作
![](/icons/93126dou.gif)
最后把变量内容写回Main Memory
![](/icons/93126dou2.gif)
Java语言规范标准(JLS)中对线程和主存互操作定义了6个行为
![](/icons/93126dou.gif)
分别为load
![](/icons/93126dou.gif)
save
![](/icons/93126dou.gif)
read
![](/icons/93126dou.gif)
write
![](/icons/93126dou.gif)
assign和use
![](/icons/93126dou.gif)
这些操作行为具有原子性
![](/icons/93126dou.gif)
且相互依赖
![](/icons/93126dou.gif)
有明确
![](/icons/93126de.gif)
![](/icons/93126diaoyong.gif)
先后顺序
![](/icons/93126dou2.gif)
具体
![](/icons/93126de.gif)
描述请参见JLS第17章
![](/icons/93126dou2.gif)
我们在前面
![](/icons/93126de.gif)
章节介绍了synchronized
![](/icons/93126de.gif)
作用
![](/icons/93126dou.gif)
现在
![](/icons/93126dou.gif)
从JMM
![](/icons/93126de.gif)
角度来重新审视synchronized关键字
![](/icons/93126dou2.gif)
假设某条线程执行
![](/icons/93126yi.gif)
个synchronized代码段
![](/icons/93126dou.gif)
其间对某变量进行操作
![](/icons/93126dou.gif)
JVM会依次执行如下动作:
(1) 获取同步对象monitor (lock)
(2) 从主存复制变量到当前工作内存 (read and load)
(3) 执行代码
![](/icons/93126dou.gif)
改变共享变量值 (use and assign)
(4) 用工作内存数据刷新主存相关内容 (store and write)
(5) 释放同步对象锁 (unlock)
可见
![](/icons/93126dou.gif)
synchronized
![](/icons/93126de.gif)
另外
![](/icons/93126yi.gif)
个作用是保证主存内容和线程
![](/icons/93126de.gif)
工作内存中
![](/icons/93126de.gif)
数据
![](/icons/93126de.gif)
![](/icons/93126yi.gif)
致性
![](/icons/93126dou2.gif)
如果没有使用synchronized关键字
![](/icons/93126dou.gif)
JVM不保证第2步和第4步会严格按照上述次序立即执行
![](/icons/93126dou2.gif)
![](/icons/93126yinwei.gif)
根据JLS中
![](/icons/93126de.gif)
规定
![](/icons/93126dou.gif)
线程
![](/icons/93126de.gif)
工作内存和主存的间
![](/icons/93126de.gif)
数据交换是松耦合
![](/icons/93126de.gif)
![](/icons/93126dou.gif)
什么时候需要刷新工作内存或者更新主内存内容
![](/icons/93126dou.gif)
可以由具体
![](/icons/93126de.gif)
虚拟机实现自行决定
![](/icons/93126dou2.gif)
如果多个线程同时执行
![](/icons/93126yi.gif)
段未经synchronized保护
![](/icons/93126de.gif)
代码段
![](/icons/93126dou.gif)
很有可能某条线程已经改动了变量
![](/icons/93126de.gif)
值
![](/icons/93126dou.gif)
但是其他线程却无法看到这个改动
![](/icons/93126dou.gif)
依然在旧
![](/icons/93126de.gif)
变量值上进行运算
![](/icons/93126dou.gif)
最终导致不可预料
![](/icons/93126de.gif)
运算结果
![](/icons/93126dou2.gif)
2、DCL失效
这
![](/icons/93126yi.gif)
节我们要讨论
![](/icons/93126de.gif)
是
![](/icons/93126yi.gif)
个让Java丢脸
![](/icons/93126de.gif)
话题:DCL失效
![](/icons/93126dou2.gif)
在开始讨论的前
![](/icons/93126dou.gif)
先介绍
![](/icons/93126yi.gif)
下LazyLoad
![](/icons/93126dou.gif)
这种窍门技巧很常用
![](/icons/93126dou.gif)
就是指
![](/icons/93126yi.gif)
个类包含某个成员变量
![](/icons/93126dou.gif)
在类
![](/icons/93126chushi.gif)
化
![](/icons/93126de.gif)
时候并不立即为该变量
![](/icons/93126chushi.gif)
化
![](/icons/93126yi.gif)
个例子
![](/icons/93126dou.gif)
而是等到真正要使用到该变量
![](/icons/93126de.gif)
时候才
![](/icons/93126chushi.gif)
化的
![](/icons/93126dou2.gif)
例如下面
![](/icons/93126de.gif)
代码:
代码1
![](/icons/93126class.gif)
Foo { private Resource res = null; public Resource getResource
![](/icons/93126kh.gif)
{
![](/icons/93126if.gif)
(res
![](/icons/93126dd.gif)
null) res =
![](/icons/93126new.gif)
Resource
![](/icons/93126kh.gif)
;
![](/icons/93126return.gif)
res; }}
由于LazyLoad可以有效
![](/icons/93126de.gif)
减少系统资源消耗
![](/icons/93126dou.gif)
提高
![](/icons/93126chengxu.gif)
整体
![](/icons/93126de.gif)
性能
![](/icons/93126dou.gif)
所以被广泛
![](/icons/93126de.gif)
使用
![](/icons/93126dou.gif)
连Java
![](/icons/93126de.gif)
缺省类加载器也采用这种思路方法来加载Java类
![](/icons/93126dou2.gif)
在单线程环境下
![](/icons/93126dou.gif)
![](/icons/93126yi.gif)
切都相安无事
![](/icons/93126dou.gif)
但如果把上面
![](/icons/93126de.gif)
代码放到多线程环境下运行
![](/icons/93126dou.gif)
那么就可能会出现问题
![](/icons/93126dou2.gif)
假设有2条线程
![](/icons/93126dou.gif)
同时执行到了
![](/icons/93126if.gif)
(res
![](/icons/93126dd.gif)
null)
![](/icons/93126dou.gif)
那么很有可能res被
![](/icons/93126chushi.gif)
化2次
![](/icons/93126dou.gif)
为了避免这样
![](/icons/93126de.gif)
Race Condition
![](/icons/93126dou.gif)
得用synchronized关键字把上面
![](/icons/93126de.gif)
思路方法同步起来
![](/icons/93126dou2.gif)
代码如下:
代码2
Class Foo { Private Resource res = null; Public synchronized Resource getResource
![](/icons/93126kh.gif)
{ If (res
![](/icons/93126dd.gif)
null) res =
![](/icons/93126new.gif)
Resource
![](/icons/93126kh.gif)
;
![](/icons/93126return.gif)
res; }}
现在Race Condition解决了
![](/icons/93126dou.gif)
![](/icons/93126yi.gif)
切都很好
![](/icons/93126dou2.gif)
N天过后
![](/icons/93126dou.gif)
好学
![](/icons/93126de.gif)
你偶然看了
![](/icons/93126yi.gif)
本Refactoring
![](/icons/93126de.gif)
魔书
![](/icons/93126dou.gif)
深深为的打动
![](/icons/93126dou.gif)
准备自己尝试这重构
![](/icons/93126yi.gif)
些以前写过
![](/icons/93126de.gif)
![](/icons/93126chengxu.gif)
![](/icons/93126dou.gif)
于是找到了上面这段代码
![](/icons/93126dou2.gif)
你已经不再是以前
![](/icons/93126de.gif)
Java菜鸟
![](/icons/93126dou.gif)
深知synchronized过
![](/icons/93126de.gif)
思路方法在速度上要比未同步
![](/icons/93126de.gif)
思路方法慢上100倍
![](/icons/93126dou.gif)
同时你也发现
![](/icons/93126dou.gif)
只有第
![](/icons/93126yi.gif)
次
![](/icons/93126diaoyong.gif)
该思路方法
![](/icons/93126de.gif)
时候才需要同步
![](/icons/93126dou.gif)
而
![](/icons/93126yi.gif)
旦res
![](/icons/93126chushi.gif)
化完成
![](/icons/93126dou.gif)
同步完全没必要
![](/icons/93126dou2.gif)
所以你很快就把代码重构成了下面
![](/icons/93126de.gif)
样子:
代码3
Class Foo {Private Resource res = null; Public Resource getResource
![](/icons/93126kh.gif)
{ If (res
![](/icons/93126dd.gif)
null){ synchronized(this){
![](/icons/93126if.gif)
(res
![](/icons/93126dd.gif)
null){ res =
![](/icons/93126new.gif)
Resource
![](/icons/93126kh.gif)
;}} }
![](/icons/93126return.gif)
res; }}
这种看起来很完美
![](/icons/93126de.gif)
优化窍门技巧就是Double-Checked Locking
![](/icons/93126dou2.gif)
但是很遗憾
![](/icons/93126dou.gif)
根据Java
![](/icons/93126de.gif)
语言规范标准
![](/icons/93126dou.gif)
上面
![](/icons/93126de.gif)
代码是不可靠
![](/icons/93126de.gif)
![](/icons/93126dou2.gif)
造成DCL失效
![](/icons/93126de.gif)
原因的
![](/icons/93126yi.gif)
是编译器
![](/icons/93126de.gif)
优化会调整代码
![](/icons/93126de.gif)
次序
![](/icons/93126dou2.gif)
只要是在单个线程情况下执行结果是正确
![](/icons/93126de.gif)
![](/icons/93126dou.gif)
就可以认为编译器这样
![](/icons/93126de.gif)
“自作主张
![](/icons/93126de.gif)
调整代码次序”
![](/icons/93126de.gif)
行为是合法
![](/icons/93126de.gif)
![](/icons/93126dou2.gif)
JLS在某些方面
![](/icons/93126de.gif)
规定比较自由
![](/icons/93126dou.gif)
就是为了让JVM有更多余地进行代码优化以提高执行效率
![](/icons/93126dou2.gif)
而现在
![](/icons/93126de.gif)
CPU大多使用超流水线技术来加快代码执行速度
![](/icons/93126dou.gif)
针对这样
![](/icons/93126de.gif)
CPU
![](/icons/93126dou.gif)
编译器采取
![](/icons/93126de.gif)
代码优化
![](/icons/93126de.gif)
思路方法的
![](/icons/93126yi.gif)
就是在调整某些代码
![](/icons/93126de.gif)
次序
![](/icons/93126dou.gif)
尽可能保证在
![](/icons/93126chengxu.gif)
执行
![](/icons/93126de.gif)
时候不要让CPU
![](/icons/93126de.gif)
指令流水线断流
![](/icons/93126dou.gif)
从而提高
![](/icons/93126chengxu.gif)
![](/icons/93126de.gif)
执行速度
![](/icons/93126dou2.gif)
正是这样
![](/icons/93126de.gif)
代码调整会导致DCL
![](/icons/93126de.gif)
失效
![](/icons/93126dou2.gif)
为了进
![](/icons/93126yi.gif)
步证明这个问题
![](/icons/93126dou.gif)
引用
![](/icons/93126yi.gif)
下
![](/icons/93126smhl.gif)
DCL Broken Declaration
![](/icons/93126smhr.gif)
文章中
![](/icons/93126de.gif)
例子:
设
![](/icons/93126yi.gif)
行Java代码:
Objects[i].reference =
![](/icons/93126new.gif)
Object
![](/icons/93126kh.gif)
;
经过Symantec JIT编译器编译过以后
![](/icons/93126dou.gif)
最终会变成如下汇编码在机器中执行:
0206106A mov eax,0F97E78h0206106F call 01F6B210 ;为Object申请内存空间 ; 返回值放在eax中02061074 mov dword ptr [ebp],eax ; EBP 中是objects[i].reference
![](/icons/93126de.gif)
地址 ; 将返回
![](/icons/93126de.gif)
空间地址放入其中 ; 此时Object尚未
![](/icons/93126chushi.gif)
化02061077 mov ecx,dword ptr [eax] ; dereference eax所指向
![](/icons/93126de.gif)
内容 ; 获得新创建对象
![](/icons/93126de.gif)
起始地址02061079 mov dword ptr [ecx],100h ; 下面4行是内联
![](/icons/93126de.gif)
构造
![](/icons/93126hanshu.gif)
0206107F mov dword ptr [ecx+4],200h 02061086 mov dword ptr [ecx+8],400h0206108D mov dword ptr [ecx+0Ch],0F84030h
可见
![](/icons/93126dou.gif)
Object构造
![](/icons/93126hanshu.gif)
尚未
![](/icons/93126diaoyong.gif)
![](/icons/93126dou.gif)
但是已经能够通过objects[i].reference获得Object对象例子
![](/icons/93126de.gif)
引用
![](/icons/93126dou2.gif)
如果把代码放到多线程环境下运行
![](/icons/93126dou.gif)
某线程在执行到该行代码
![](/icons/93126de.gif)
时候JVM或者操作系统进行了
![](/icons/93126yi.gif)
次线程切换
![](/icons/93126dou.gif)
其他线程显然会发现msg对象已经不为空
![](/icons/93126dou.gif)
导致Lazy load
![](/icons/93126de.gif)
判断语句
![](/icons/93126if.gif)
(objects[i].reference
![](/icons/93126dd.gif)
null)不成立
![](/icons/93126dou2.gif)
线程认为对象已经建立成功
![](/icons/93126dou.gif)
随的可能会使用对象
![](/icons/93126de.gif)
成员变量或者
![](/icons/93126diaoyong.gif)
该对象例子
![](/icons/93126de.gif)
思路方法
![](/icons/93126dou.gif)
最终导致不可预测
![](/icons/93126de.gif)
![](/icons/93126cuowu.gif)
![](/icons/93126dou2.gif)
原因的 2是在共享内存
![](/icons/93126de.gif)
SMP机上
![](/icons/93126dou.gif)
每个CPU有自己
![](/icons/93126de.gif)
Cache和寄存器
![](/icons/93126dou.gif)
共享同
![](/icons/93126yi.gif)
个系统内存
![](/icons/93126dou2.gif)
所以CPU可能会动态调整指令
![](/icons/93126de.gif)
执行次序
![](/icons/93126dou.gif)
以更好
![](/icons/93126de.gif)
进行并行运算并且把运算结果和主内存同步
![](/icons/93126dou2.gif)
这样
![](/icons/93126de.gif)
代码次序调整也可能导致DCL失效
![](/icons/93126dou2.gif)
回想
![](/icons/93126yi.gif)
下前面对Java内存模型
![](/icons/93126de.gif)
介绍
![](/icons/93126dou.gif)
我们这里可以把Main Memory看作系统
![](/icons/93126de.gif)
物理内存
![](/icons/93126dou.gif)
把Thread Working Memory认为是CPU内部
![](/icons/93126de.gif)
Cache和寄存器
![](/icons/93126dou.gif)
没有synchronized
![](/icons/93126de.gif)
保护
![](/icons/93126dou.gif)
Cache和寄存器
![](/icons/93126de.gif)
内容就不会及时和主内存
![](/icons/93126de.gif)
内容同步
![](/icons/93126dou.gif)
从而导致
![](/icons/93126yi.gif)
条线程无法看到另
![](/icons/93126yi.gif)
条线程对
![](/icons/93126yi.gif)
些变量
![](/icons/93126de.gif)
改动
![](/icons/93126dou2.gif)
结合代码3来举例介绍说明
![](/icons/93126dou.gif)
假设Resource类
![](/icons/93126de.gif)
实现如下:
Class Resource{ Object obj;}
即Resource类有
![](/icons/93126yi.gif)
个obj成员变量引用了Object
![](/icons/93126de.gif)
![](/icons/93126yi.gif)
个例子
![](/icons/93126dou2.gif)
假设2条线程在运行
![](/icons/93126dou.gif)
其状态用如下简化图表示:
![](http://www.crazycoder.cn/WebFiles/200812/8236a53c-1c10-4214-96df-ad618cda6731.g<img)
>
图2
现在Thread-1构造了Resource例子
![](/icons/93126dou.gif)
![](/icons/93126chushi.gif)
化过程中改动了obj
![](/icons/93126de.gif)
![](/icons/93126yi.gif)
些内容
![](/icons/93126dou2.gif)
退出同步代码段后
![](/icons/93126dou.gif)
![](/icons/93126yinwei.gif)
采取了同步机制
![](/icons/93126dou.gif)
Thread-1所做
![](/icons/93126de.gif)
改动都会反映到主存中
![](/icons/93126dou2.gif)
接下来Thread-2获得了新
![](/icons/93126de.gif)
Resource例子变量res
![](/icons/93126dou.gif)
由于没有使用synchronized保护所以Thread-2不会进行刷新工作内存
![](/icons/93126de.gif)
操作
![](/icons/93126dou2.gif)
假如的前Thread-2
![](/icons/93126de.gif)
工作内存中已经有了obj例子
![](/icons/93126de.gif)
![](/icons/93126yi.gif)
份拷贝
![](/icons/93126dou.gif)
那么Thread-2在对obj执行use操作
![](/icons/93126de.gif)
时候就不会去执行load操作,这样
![](/icons/93126yi.gif)
来就无法看到Thread-1对obj
![](/icons/93126de.gif)
改变
![](/icons/93126dou.gif)
这显然会导致
![](/icons/93126cuowu.gif)
![](/icons/93126de.gif)
运算结果
![](/icons/93126dou2.gif)
此外
![](/icons/93126dou.gif)
Thread-1在退出同步代码段
![](/icons/93126de.gif)
时刻对ref和obj执行
![](/icons/93126de.gif)
写入主存
![](/icons/93126de.gif)
操作次序也是不确定
![](/icons/93126de.gif)
![](/icons/93126dou.gif)
所以即使Thread-2对obj执行了load操作
![](/icons/93126dou.gif)
也有可能只读到obj
![](/icons/93126de.gif)
初试状态
![](/icons/93126de.gif)
数据
![](/icons/93126dou2.gif)
(注:这里
![](/icons/93126de.gif)
load/use均指JMM定义
![](/icons/93126de.gif)
操作)
有很多人不死心
![](/icons/93126dou.gif)
试图想出了很多精妙
![](/icons/93126de.gif)
办法来解决这个问题
![](/icons/93126dou.gif)
但最终都失败了
![](/icons/93126dou2.gif)
事实上
![](/icons/93126dou.gif)
无论是目前
![](/icons/93126de.gif)
JMM还是已经作为JSR提交
![](/icons/93126de.gif)
JMM模型
![](/icons/93126de.gif)
增强
![](/icons/93126dou.gif)
DCL都不能正常使用
![](/icons/93126dou2.gif)
在William Pugh
![](/icons/93126de.gif)
论文
![](/icons/93126smhl.gif)
Fixing the Java Memory Model
![](/icons/93126smhr.gif)
中详细
![](/icons/93126de.gif)
探讨了JMM
![](/icons/93126de.gif)
![](/icons/93126yi.gif)
些硬伤
![](/icons/93126dou.gif)
更尝试给出
![](/icons/93126yi.gif)
个新
![](/icons/93126de.gif)
内存模型
![](/icons/93126dou.gif)
有兴趣深入研究
![](/icons/93126de.gif)
读者可以参见文后
![](/icons/93126de.gif)
参考资料
![](/icons/93126dou2.gif)
如果你设计
![](/icons/93126de.gif)
对象在
![](/icons/93126chengxu.gif)
中只有
![](/icons/93126yi.gif)
个例子
![](/icons/93126dou.gif)
即singleton
![](/icons/93126de.gif)
![](/icons/93126dou.gif)
有
![](/icons/93126yi.gif)
种可行
![](/icons/93126de.gif)
解决办法来实现其LazyLoad:就是利用类加载器
![](/icons/93126de.gif)
LazyLoad特性
![](/icons/93126dou2.gif)
代码如下:
Class ResSingleton {public
![](/icons/93126static.gif)
Resource res =
![](/icons/93126new.gif)
Resource
![](/icons/93126kh.gif)
;}
这里ResSingleton只有
![](/icons/93126yi.gif)
个静态成员变量
![](/icons/93126dou2.gif)
当第
![](/icons/93126yi.gif)
次使用ResSingleton.res
![](/icons/93126de.gif)
时候
![](/icons/93126dou.gif)
JVM才会
![](/icons/93126chushi.gif)
化
![](/icons/93126yi.gif)
个Resource例子
![](/icons/93126dou.gif)
并且JVM会保证
![](/icons/93126chushi.gif)
化
![](/icons/93126de.gif)
结果及时写入主存
![](/icons/93126dou.gif)
能让其他线程看到
![](/icons/93126dou.gif)
这样就成功
![](/icons/93126de.gif)
实现了LazyLoad
![](/icons/93126dou2.gif)
除了这个办法以外
![](/icons/93126dou.gif)
还可以使用ThreadLocal来实现DCL
![](/icons/93126de.gif)
思路方法
![](/icons/93126dou.gif)
但是由于ThreadLocal
![](/icons/93126de.gif)
实现效率比较低
![](/icons/93126dou.gif)
所以这种解决办法会有较大
![](/icons/93126de.gif)
性能损失
![](/icons/93126dou.gif)
有兴趣
![](/icons/93126de.gif)
读者可以参考文后
![](/icons/93126de.gif)
参考资料
![](/icons/93126dou2.gif)
最后要介绍说明
![](/icons/93126de.gif)
是
![](/icons/93126dou.gif)
对于DCL是否有效
![](/icons/93126dou.gif)
个人认为更多
![](/icons/93126de.gif)
是
![](/icons/93126yi.gif)
种带有学究气
![](/icons/93126de.gif)
推断和讨论
![](/icons/93126dou2.gif)
而从纯理论
![](/icons/93126de.gif)
角度来看
![](/icons/93126dou.gif)
存取任何可能共享
![](/icons/93126de.gif)
变量(对象引用)都需要同步保护
![](/icons/93126dou.gif)
否则都有可能出错
![](/icons/93126dou.gif)
但是处处用synchronized又会增加死锁
![](/icons/93126de.gif)
发生几率
![](/icons/93126dou.gif)
苦命
![](/icons/93126de.gif)
![](/icons/93126chengxu.gif)
员如何来解决这个矛盾呢?事实上
![](/icons/93126dou.gif)
在很多Java开源项目(比如Ofbiz/Jive等)
![](/icons/93126de.gif)
代码中都能找到使用DCL
![](/icons/93126de.gif)
证据
![](/icons/93126dou.gif)
我在具体
![](/icons/93126de.gif)
实战中也没有碰到过因DCL而发生
![](/icons/93126de.gif)
![](/icons/93126chengxu.gif)
异常
![](/icons/93126dou2.gif)
个人
![](/icons/93126de.gif)
偏好是:不妨先大胆使用DCL
![](/icons/93126dou.gif)
等出现问题再用synchronized逐步排除的
![](/icons/93126dou2.gif)
也许有人偏于保守
![](/icons/93126dou.gif)
认为稳定压倒
![](/icons/93126yi.gif)
切
![](/icons/93126dou.gif)
那就不妨先用synchronized同步起来
![](/icons/93126dou.gif)
我想这是
![](/icons/93126yi.gif)
个见仁见智
![](/icons/93126de.gif)
问题
![](/icons/93126dou.gif)
而且得针对具体
![](/icons/93126de.gif)
项目具体分析后才能决定
![](/icons/93126dou2.gif)
还有
![](/icons/93126yi.gif)
个办法就是写
![](/icons/93126yi.gif)
个测试案例来测试
![](/icons/93126yi.gif)
下系统是否存在DCL现象
![](/icons/93126dou.gif)
附带
![](/icons/93126de.gif)
光盘中提供了这样
![](/icons/93126yi.gif)
个例子
![](/icons/93126dou.gif)
感兴趣
![](/icons/93126de.gif)
读者可以自行编译测试
![](/icons/93126dou2.gif)
不管结果怎样
![](/icons/93126dou.gif)
这样
![](/icons/93126de.gif)
讨论有助于我们更好
![](/icons/93126de.gif)
认识JMM
![](/icons/93126dou.gif)
养成用多线程
![](/icons/93126de.gif)
思路去分析问题
![](/icons/93126de.gif)
习惯
![](/icons/93126dou.gif)
提高我们
![](/icons/93126de.gif)
![](/icons/93126chengxu.gif)
设计能力
![](/icons/93126dou2.gif)
3、Java线程同步增强包
相信你已经了解了Java用于同步
![](/icons/93126de.gif)
3板斧:synchronized/wait/not
![](/icons/93126if.gif)
y
![](/icons/93126dou.gif)
它们
![](/icons/93126de.gif)
确简单而有效
![](/icons/93126dou2.gif)
但是在某些情况下
![](/icons/93126dou.gif)
我们需要更加复杂
![](/icons/93126de.gif)
同步工具
![](/icons/93126dou2.gif)
有些简单
![](/icons/93126de.gif)
同步工具类
![](/icons/93126dou.gif)
诸如ThreadBarrier
![](/icons/93126dou.gif)
Semaphore
![](/icons/93126dou.gif)
ReadWriteLock等
![](/icons/93126dou.gif)
可以自己编程实现
![](/icons/93126dou2.gif)
现在要介绍
![](/icons/93126de.gif)
是牛人Doug Lea
![](/icons/93126de.gif)
Concurrent包
![](/icons/93126dou2.gif)
这个包专门为实现Java高级并行
![](/icons/93126chengxu.gif)
所开发
![](/icons/93126dou.gif)
可以满足我们绝大部分
![](/icons/93126de.gif)
要求
![](/icons/93126dou2.gif)
更令人兴奋
![](/icons/93126de.gif)
是
![](/icons/93126dou.gif)
这个包公开源代码
![](/icons/93126dou.gif)
可自由下载
![](/icons/93126dou2.gif)
且在JDK1.5中该包将作为SDK
![](/icons/93126yi.gif)
部分提供给Java开发人员
![](/icons/93126dou2.gif)
Concurrent Package提供了
![](/icons/93126yi.gif)
系列基本
![](/icons/93126de.gif)
操作接口
![](/icons/93126dou.gif)
包括sync
![](/icons/93126dou.gif)
channel
![](/icons/93126dou.gif)
executor,barrier,callable等
![](/icons/93126dou2.gif)
这里将对前 3种接口及其部分派生类进行简单
![](/icons/93126de.gif)
介绍
![](/icons/93126dou2.gif)
sync接口:专门负责同步操作
![](/icons/93126dou.gif)
用于替代Java提供
![](/icons/93126de.gif)
synchronized关键字
![](/icons/93126dou.gif)
以实现更加灵活
![](/icons/93126de.gif)
代码同步
![](/icons/93126dou2.gif)
其类关系图如下:
![](http://www.crazycoder.cn/WebFiles/200812/31a717bf-185a-4d72-8e0d-6e5426114c98.jpg)
图3 Concurrent包Sync接口类关系图
Semaphore:和前面介绍
![](/icons/93126de.gif)
代码类似
![](/icons/93126dou.gif)
可用于pool类实现资源管理限制
![](/icons/93126dou2.gif)
提供了acquire
![](/icons/93126kh.gif)
思路方法允许在设定时间内尝试锁定信号量
![](/icons/93126dou.gif)
若超时则返回false
![](/icons/93126dou2.gif)
Mutex:和Java
![](/icons/93126de.gif)
synchronized类似
![](/icons/93126dou.gif)
和的区别
![](/icons/93126de.gif)
是
![](/icons/93126dou.gif)
synchronized
![](/icons/93126de.gif)
同步段只能限制在
![](/icons/93126yi.gif)
个思路方法内
![](/icons/93126dou.gif)
而Mutex对象可以作为参数在思路方法间传递
![](/icons/93126dou.gif)
所以可以把同步代码范围扩大到跨思路方法甚至跨对象
![](/icons/93126dou2.gif)
NullSync:
![](/icons/93126yi.gif)
个比较奇怪
![](/icons/93126de.gif)
东西
![](/icons/93126dou.gif)
其思路方法
![](/icons/93126de.gif)
内部实现都是空
![](/icons/93126de.gif)
![](/icons/93126dou.gif)
可能是作者认为如果你在实际中发现某段代码根本可以不用同步
![](/icons/93126dou.gif)
但是又不想过多改动这段代码
![](/icons/93126dou.gif)
那么就可以用NullSync来替代原来
![](/icons/93126de.gif)
Sync例子
![](/icons/93126dou2.gif)
此外
![](/icons/93126dou.gif)
由于NullSync
![](/icons/93126de.gif)
思路方法都是synchronized
![](/icons/93126dou.gif)
所以还是保留了“内存壁垒”
![](/icons/93126de.gif)
特性
![](/icons/93126dou2.gif)
ObservableSync:把sync和observer模式结合起来
![](/icons/93126dou.gif)
当sync
![](/icons/93126de.gif)
思路方法被
![](/icons/93126diaoyong.gif)
时
![](/icons/93126dou.gif)
把消息通知给订阅者
![](/icons/93126dou.gif)
可用于同步性能调试
![](/icons/93126dou2.gif)
TimeoutSync:可以认为是
![](/icons/93126yi.gif)
个adaptor
![](/icons/93126dou.gif)
其构造
![](/icons/93126hanshu.gif)
如下:
public TimeoutSync(Sync sync, long timeout){…}
具体上锁
![](/icons/93126de.gif)
代码靠构造
![](/icons/93126hanshu.gif)
传入
![](/icons/93126de.gif)
sync例子来完成
![](/icons/93126dou.gif)
其自身只负责监测上锁操作是否超时
![](/icons/93126dou.gif)
可和SyncSet合用
![](/icons/93126dou2.gif)
Channel接口:代表
![](/icons/93126yi.gif)
种具备同步控制能力
![](/icons/93126de.gif)
容器
![](/icons/93126dou.gif)
你可以从中存放/读取对象
![](/icons/93126dou2.gif)
区别于JDK中
![](/icons/93126de.gif)
Collection接口
![](/icons/93126dou.gif)
可以把Channel看作是连接对象构造者(Producer)和对象使用者(Consumer)的间
![](/icons/93126de.gif)
![](/icons/93126yi.gif)
根管道
![](/icons/93126dou2.gif)
如图所示:
![](http://www.crazycoder.cn/WebFiles/200812/feb37533-64fc-4c7c-a06a-879e2f258fdd.jpg)
图4 Concurrent包Channel接口示意图
通过和Sync接口配合
![](/icons/93126dou.gif)
Channel提供了阻塞式
![](/icons/93126de.gif)
对象存取思路方法(put/take)以及可设置阻塞等待时间
![](/icons/93126de.gif)
offer/poll思路方法
![](/icons/93126dou2.gif)
实现Channel接口
![](/icons/93126de.gif)
类有LinkedQueue
![](/icons/93126dou.gif)
BoundedLinkedQueue
![](/icons/93126dou.gif)
BoundedBuffer
![](/icons/93126dou.gif)
BoundedPriorityQueue
![](/icons/93126dou.gif)
SynchronousChannel
![](/icons/93126dou.gif)
Slot等
![](http://www.crazycoder.cn/WebFiles/200812/34ac6d9e-af19-4de8-b92a-d2564d4f9595.jpg)
图5 Concurrent包Channel接口部分类关系图
使用Channel我们可以很容易
![](/icons/93126de.gif)
编写具备消息队列功能
![](/icons/93126de.gif)
代码
![](/icons/93126dou.gif)
举例如下:
代码4
Package org.javaresearch.j2seimproved.thread;Import EDU.oswego.cs.dl.util.concurrent.*;public
![](/icons/93126class.gif)
TestChannel { final Channel msgQ =
![](/icons/93126new.gif)
LinkedQueue
![](/icons/93126kh.gif)
; //log信息队列 public
![](/icons/93126static.gif)
void
![](/icons/93126main.gif)
(String
![](/icons/93126zhk2.gif)
args) { TestChannel tc =
![](/icons/93126new.gif)
TestChannel
![](/icons/93126kh.gif)
; For(
![](/icons/93126int.gif)
i = 0;i < 10;i
![](/icons/93126jiajia.gif)
){ Try{ tc.serve
![](/icons/93126kh.gif)
; Thread.sleep(1000); }catch(InterruptedException ie){ } } } public void serve
![](/icons/93126kh.gif)
throws InterruptedException { String status = doService
![](/icons/93126kh.gif)
;//把doService
![](/icons/93126kh.gif)
返回状态放入Channel
![](/icons/93126dou.gif)
后台logger线程自动读取的 msgQ.put(status); } private String doService
![](/icons/93126kh.gif)
{ // Do service here
![](/icons/93126return.gif)
"service completed OK! "; } public TestChannel
![](/icons/93126kh.gif)
{ // start background thread Runnable logger =
![](/icons/93126new.gif)
Runnable
![](/icons/93126kh.gif)
{ public void run
![](/icons/93126kh.gif)
{ try { for (; ; )
![](/icons/93126System.gif)
.out.pr
![](/icons/93126int.gif)
ln("Logger: " + msgQ.take
![](/icons/93126kh.gif)
); } catch (InterruptedException ie) {} } };
![](/icons/93126new.gif)
Thread(logger).start
![](/icons/93126kh.gif)
; }}
Excutor/ThreadFactory接口: 把相关
![](/icons/93126de.gif)
线程创建/回收/维护/调度等工作封装起来
![](/icons/93126dou.gif)
而让
![](/icons/93126diaoyong.gif)
者只专心于具体任务
![](/icons/93126de.gif)
编码工作(即实现Runnable接口)
![](/icons/93126dou.gif)
不必显式创建Thread类例子就能异步执行任务
![](/icons/93126dou2.gif)
使用Executor还有
![](/icons/93126yi.gif)
个好处
![](/icons/93126dou.gif)
就是实现线程
![](/icons/93126de.gif)
“轻量级”使用
![](/icons/93126dou2.gif)
前面章节曾提到
![](/icons/93126dou.gif)
即使我们实现了Runnable接口
![](/icons/93126dou.gif)
要真正
![](/icons/93126de.gif)
创建线程
![](/icons/93126dou.gif)
还是得通过
![](/icons/93126new.gif)
Thread
![](/icons/93126kh.gif)
来完成
![](/icons/93126dou.gif)
在这种情况下
![](/icons/93126dou.gif)
Runnable对象(任务)和Thread对象(线程)是1对1
![](/icons/93126de.gif)
关系
![](/icons/93126dou2.gif)
如果任务多而简单
![](/icons/93126dou.gif)
完全可以给每条线程配备
![](/icons/93126yi.gif)
个任务队列
![](/icons/93126dou.gif)
让Runnable对象(任务)和Executor对象变成n:1
![](/icons/93126de.gif)
关系
![](/icons/93126dou2.gif)
使用了Executor
![](/icons/93126dou.gif)
我们可以把上面两种线程策略都封装到具体
![](/icons/93126de.gif)
Executor实现中
![](/icons/93126dou.gif)
方便代码
![](/icons/93126de.gif)
实现和维护
![](/icons/93126dou2.gif)
具体
![](/icons/93126de.gif)
实现有: PooledExecutor
![](/icons/93126dou.gif)
ThreadedExecutor
![](/icons/93126dou.gif)
QueuedExecutor
![](/icons/93126dou.gif)
FJTaskRunnerGroup等
类关系图如下:
![](http://www.crazycoder.cn/WebFiles/200812/da7b1f52-78dd-46d3-ae16-d091b3bdc2aa.jpg)
图6 Concurrent包Executor/ThreadFactory接口部分类关系图
下面给出
![](/icons/93126yi.gif)
段代码
![](/icons/93126dou.gif)
使用PooledExecutor实现
![](/icons/93126yi.gif)
个简单
![](/icons/93126de.gif)
多线程服务器
代码5
package org.javaresearch.j2seimproved.thread;import java.net.*;import EDU.oswego.cs.dl.util.concurrent.*;public
![](/icons/93126class.gif)
TestExecutor { public
![](/icons/93126static.gif)
void
![](/icons/93126main.gif)
(String
![](/icons/93126zhk2.gif)
args) { PooledExecutor pool =
![](/icons/93126new.gif)
PooledExecutor(
![](/icons/93126new.gif)
BoundedBuffer(10), 20); pool.createThreads(4); try { ServerSocket
![](/icons/93126socket.gif)
=
![](/icons/93126new.gif)
ServerSocket(9999); for (; ; ) { final Socket connection =
![](/icons/93126socket.gif)
.accept
![](/icons/93126kh.gif)
; pool.execute(
![](/icons/93126new.gif)
Runnable
![](/icons/93126kh.gif)
{ public void run
![](/icons/93126kh.gif)
{
![](/icons/93126new.gif)
Handler
![](/icons/93126kh.gif)
.process(connection); } }); } } catch (Exception e) {} // die }
![](/icons/93126class.gif)
Handler { void process(Socket s){ } }}