概述
假如行为不同步在多线程环境下就会出现混乱。
在共享内存环境中读取一块由可能被多个线程更新的数据。那么就需要建立java内存模型。并且这个模型在不同的硬件架构中是类似的。
以下为原文:
The behavior of threads, particularly when not correctly synchronized, can be
confusing and counterintuitive. This chapter describes the semantics of multithreaded
programs; it includes rules for which values may be seen by a read of
shared memory that is updated by multiple threads. As the specification is similar
to the memory models for different hardware architectures, these semantics are
known as the Java programming language memory model. When no confusion
can arise, we will simply refer to these rules as "the memory model".
1. 锁
比较简单,同步由监视器实现,注意监视器关联的对象是哪个就可以了。
java.util.concurrent包提供了同步的其他方式。
2. 代码重排序问题
初始化r1=r2=0
Trace 17.1: Surprising results caused by statement reordering - original code
Thread 1 Thread 2
1: r2 = A; 3: r1 = B;
2: B = 1; 4: A = 2;
Trace 17.2: Surprising results caused by statement reordering - valid compiler
transformation
Thread 1 Thread 2
B = 1; r1 = B;
3. r2 = A; A = 2;
初始化rp=q,p.x=0
Trace 17.3: Surprising results caused by forward substitution
Thread 1 Thread 2
r1 = p; r6 = p;
r2 = r1.x; r6.x = 3;
r3 = q;
r4 = r3.x;
r5 = r1.x;
Trace 17.4: Surprising results caused by forward substitution
Thread 1 Thread 2
r1 = p; r6 = p;
r2 = r1.x; r6.x = 3;
r3 = q;
r4 = r3.x;
r5 = r2;
4. 内存模型
java 内存模型 ( java memory model )
根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。
每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。
[img]http://dl.iteye.com/upload/attachment/231445/b7c26a66-1e97-3aeb-80bb-b3067359289f.jpg[/img]
其中, 工作内存里的变量, 在多核处理器下, 将大部分储存于处理器高速缓存中, 高速缓存在不经过内存时, 也是不可见的.
jmm怎么体现 可视性(Visibility) ?
在jmm中, 通过并发线程修改变量值, 必须将线程变量同步回主存后, 其他线程才能访问到.
jmm怎么体现 有序性(Ordering) ?
通过java提供的同步机制或volatile关键字, 来保证内存的访问顺序.
缓存一致性(cache coherency)
什么是缓存一致性?
它是一种管理多处理器系统的高速缓存区结构,其可以保证数据在高速缓存区到内存的传输中不会丢失或重复。(来自wikipedia)
举例理解:
假如有一个处理器有一个更新了的变量值位于其缓存中,但还没有被写入主内存,这样别的处理器就可能会看不到这个更新的值.
解决缓存一致性的方法?
a, 顺序一致性模型:
要求某处理器对所改变的变量值立即进行传播, 并确保该值被所有处理器接受后, 才能继续执行其他指令.
b, 释放一致性模型: (类似jmm cache coherency)
允许处理器将改变的变量值延迟到释放锁时才进行传播.
happens-before ordering
a, 在程序顺序中, 线程中的每一个操作, 发生在当前操作后面将要出现的每一个操作之前.
b, 对象监视器的解锁发生在等待获取对象锁的线程之前.
c, 对volitile关键字修饰的变量写入操作, 发生在对该变量的读取之前.
d, 对一个线程的 Thread.start() 调用 发生在启动的线程中的所有操作之前.
e, 线程中的所有操作 发生在从这个线程的 Thread.join()成功返回的所有其他线程之前.
以下为原文:
The rules for happens-before are:
Program order rule. Each action in a thread happens-before every action in that thread that comes later in the program order.
Monitor lock rule. An unlock on a monitor lock happens-before every subsequent lock on that same monitor lock.
Volatile variable rule. A write to a volatile field happens-before every subsequent read of that same field.
Thread start rule. A call to Thread.start on a thread happens-before every action in the started thread.
Thread termination rule. Any action in a thread happens-before any other thread detects that thread has terminated, either by successfully return from Thread.join or by Thread.isAlive returning false.
Interruption rule. A thread calling interrupt on another thread happens-before the interrupted thread detects the interrupt (either by having InterruptedException tHRown, or invoking isInterrupted or interrupted).
Finalizer rule. The end of a constructor for an object happens-before the start of the finalizer for that object.
Transitivity. If A happens-before B, and B happens-before C, then A happens-before C.
单例的 DCL多线程延迟加载示例:
Java代码
1. Class Foo
2. {
3. Private Resource res = null;
4. Public Resource getResource()
5. {
6. If (res == null)
7. {
8. //只有在第一次初始化时,才使用同步方式.
9. synchronized(this)
10. {
11. if(res == null)
12. {
13. res = new Resource();
14. }
15. }
16. }
17. return res;
18. }
19. }
Double-Checked Locking看起来是非常完美的。但是很遗憾,根据Java的语言规范,上面的代码是不可靠的。
出现上述问题, 最重要的2个原因如下:
1, 编译器优化了程序指令, 以加快cpu处理速度.
2, 多核cpu动态调整指令顺序, 以加快并行运算能力.
问题出现的顺序:
1, 线程A, 发现对象未实例化, 准备开始实例化
2, 由于编译器优化了程序指令, 允许对象在构造函数未调用完前, 将 共享变量的引用指向 部分构造的对象, 虽然对象未完全实例化, 但已经不为null了.
3, 线程B, 发现部分构造的对象已不是null, 则直接返回了该对象.
不过, 一些著名的开源框架, 包括jive,lenya等也都在使用DCL模式, 且未见一些极端异常.
说明, DCL失效问题的出现率还是比较低的.
接下来就是性能与稳定之间的选择了?
DCL的替代 Initialize-On-Demand :
Java代码
1. public class Foo {
2. // 似有静态内部类, 只有当有引用时, 该类才会被装载
3. private static class LazyFoo {
4. public static Foo foo = new Foo();
5. }
6.
7. public static Foo getInstance() {
8. return LazyFoo.foo;
9. }
10. }
参考资料:jls3.0
http://javatar.iteye.com/blog/144763
假如行为不同步在多线程环境下就会出现混乱。
在共享内存环境中读取一块由可能被多个线程更新的数据。那么就需要建立java内存模型。并且这个模型在不同的硬件架构中是类似的。
以下为原文:
The behavior of threads, particularly when not correctly synchronized, can be
confusing and counterintuitive. This chapter describes the semantics of multithreaded
programs; it includes rules for which values may be seen by a read of
shared memory that is updated by multiple threads. As the specification is similar
to the memory models for different hardware architectures, these semantics are
known as the Java programming language memory model. When no confusion
can arise, we will simply refer to these rules as "the memory model".
1. 锁
比较简单,同步由监视器实现,注意监视器关联的对象是哪个就可以了。
java.util.concurrent包提供了同步的其他方式。
2. 代码重排序问题
初始化r1=r2=0
Trace 17.1: Surprising results caused by statement reordering - original code
Thread 1 Thread 2
1: r2 = A; 3: r1 = B;
2: B = 1; 4: A = 2;
Trace 17.2: Surprising results caused by statement reordering - valid compiler
transformation
Thread 1 Thread 2
B = 1; r1 = B;
3. r2 = A; A = 2;
初始化rp=q,p.x=0
Trace 17.3: Surprising results caused by forward substitution
Thread 1 Thread 2
r1 = p; r6 = p;
r2 = r1.x; r6.x = 3;
r3 = q;
r4 = r3.x;
r5 = r1.x;
Trace 17.4: Surprising results caused by forward substitution
Thread 1 Thread 2
r1 = p; r6 = p;
r2 = r1.x; r6.x = 3;
r3 = q;
r4 = r3.x;
r5 = r2;
4. 内存模型
java 内存模型 ( java memory model )
根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。
每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。
[img]http://dl.iteye.com/upload/attachment/231445/b7c26a66-1e97-3aeb-80bb-b3067359289f.jpg[/img]
其中, 工作内存里的变量, 在多核处理器下, 将大部分储存于处理器高速缓存中, 高速缓存在不经过内存时, 也是不可见的.
jmm怎么体现 可视性(Visibility) ?
在jmm中, 通过并发线程修改变量值, 必须将线程变量同步回主存后, 其他线程才能访问到.
jmm怎么体现 有序性(Ordering) ?
通过java提供的同步机制或volatile关键字, 来保证内存的访问顺序.
缓存一致性(cache coherency)
什么是缓存一致性?
它是一种管理多处理器系统的高速缓存区结构,其可以保证数据在高速缓存区到内存的传输中不会丢失或重复。(来自wikipedia)
举例理解:
假如有一个处理器有一个更新了的变量值位于其缓存中,但还没有被写入主内存,这样别的处理器就可能会看不到这个更新的值.
解决缓存一致性的方法?
a, 顺序一致性模型:
要求某处理器对所改变的变量值立即进行传播, 并确保该值被所有处理器接受后, 才能继续执行其他指令.
b, 释放一致性模型: (类似jmm cache coherency)
允许处理器将改变的变量值延迟到释放锁时才进行传播.
happens-before ordering
a, 在程序顺序中, 线程中的每一个操作, 发生在当前操作后面将要出现的每一个操作之前.
b, 对象监视器的解锁发生在等待获取对象锁的线程之前.
c, 对volitile关键字修饰的变量写入操作, 发生在对该变量的读取之前.
d, 对一个线程的 Thread.start() 调用 发生在启动的线程中的所有操作之前.
e, 线程中的所有操作 发生在从这个线程的 Thread.join()成功返回的所有其他线程之前.
以下为原文:
The rules for happens-before are:
Program order rule. Each action in a thread happens-before every action in that thread that comes later in the program order.
Monitor lock rule. An unlock on a monitor lock happens-before every subsequent lock on that same monitor lock.
Volatile variable rule. A write to a volatile field happens-before every subsequent read of that same field.
Thread start rule. A call to Thread.start on a thread happens-before every action in the started thread.
Thread termination rule. Any action in a thread happens-before any other thread detects that thread has terminated, either by successfully return from Thread.join or by Thread.isAlive returning false.
Interruption rule. A thread calling interrupt on another thread happens-before the interrupted thread detects the interrupt (either by having InterruptedException tHRown, or invoking isInterrupted or interrupted).
Finalizer rule. The end of a constructor for an object happens-before the start of the finalizer for that object.
Transitivity. If A happens-before B, and B happens-before C, then A happens-before C.
单例的 DCL多线程延迟加载示例:
Java代码
1. Class Foo
2. {
3. Private Resource res = null;
4. Public Resource getResource()
5. {
6. If (res == null)
7. {
8. //只有在第一次初始化时,才使用同步方式.
9. synchronized(this)
10. {
11. if(res == null)
12. {
13. res = new Resource();
14. }
15. }
16. }
17. return res;
18. }
19. }
Double-Checked Locking看起来是非常完美的。但是很遗憾,根据Java的语言规范,上面的代码是不可靠的。
出现上述问题, 最重要的2个原因如下:
1, 编译器优化了程序指令, 以加快cpu处理速度.
2, 多核cpu动态调整指令顺序, 以加快并行运算能力.
问题出现的顺序:
1, 线程A, 发现对象未实例化, 准备开始实例化
2, 由于编译器优化了程序指令, 允许对象在构造函数未调用完前, 将 共享变量的引用指向 部分构造的对象, 虽然对象未完全实例化, 但已经不为null了.
3, 线程B, 发现部分构造的对象已不是null, 则直接返回了该对象.
不过, 一些著名的开源框架, 包括jive,lenya等也都在使用DCL模式, 且未见一些极端异常.
说明, DCL失效问题的出现率还是比较低的.
接下来就是性能与稳定之间的选择了?
DCL的替代 Initialize-On-Demand :
Java代码
1. public class Foo {
2. // 似有静态内部类, 只有当有引用时, 该类才会被装载
3. private static class LazyFoo {
4. public static Foo foo = new Foo();
5. }
6.
7. public static Foo getInstance() {
8. return LazyFoo.foo;
9. }
10. }
参考资料:jls3.0
http://javatar.iteye.com/blog/144763