您的位置:首页 > 编程语言 > Java开发

Java线程模型

2016-03-30 19:56 302 查看
Java 多线程并发程序出问题非常比较难发现。测试人员在低并发的情况下,无法测试到这种情况,一到高并发上线的时候,不定时的出现问题。所以在写这些高并发应用程序应该各位小心,同时也必须深入了解Java的内存模型JSR133。本文部分参考了http://ifeve.com/memory-model/

重排序导致的问题

如下demo,假设有两个线程,线程A调用writer操作,线程B调用reader操作。那么当线程B读y=2的情况下,是否意味着r2肯定是1呢?答案是否定的。原因是A线程的writer操作可能进行过重排序,y的赋值在x的赋值前面。这也导致了r1是2,而r2可能还是0。
应用程序可能会有如下几种重排序:

编译器重排序。 编译器为了优化,遍出来的指令为经过优化,和程序员的初始代码不同。
处理器优化重排序。处理器在不影响本线程 最终结果的情况下,优化指令的执行顺序。
内存优化重排序。

class Reordering
{

int x = 0, y = 0;

public void writer()
{
x = 1;
y = 2;

}
public void reader()
{
int r1 = y;
int r2 = x;
}
}

处理器寄存器缓存导致的问题

 为了提高执行速度,变量会在处理器中有相应的缓存,并不会立即刷入主存。

同样为上节的代码,线程A更新了X的值,可能只会更新了处理线程A的处理器的寄存器,而并不会直接刷主存。 线程B从主存中读取x的值可能还是最初的老的值。
同理,处理线程B的处理器可能缓存了X的值,并没有读取最新主存中的值。

Happens-Before 原则

基于上面的问题JAVA内存模型JSR133引入了Happens-Before原则,来控制这些并发状态下的线程可见性问题。

顺序原则:一个线程中的操作,happens-before于该线程中的任意后续动作

锁原则:解锁之前的操作happens-before,后续获得该锁的操作

volatile原则: volatile的写操作happens-before 后续的读操作

传递性原则: A Happens-before B,B happens-before C,则A happens-before 

Happens-Before是不是说一定是指令先于执行? 下面代码第二句一定先于第一句执行?答案是不是的,只是优先于跟其结果相关的操作执行。

Volatile变量

volatile变量会避免处理器做任何的缓存优化,写的时候强行刷新主存内容。读的时候强行让本地缓存失效,去读主存内容。
这就保证了任何一个线程读的都是更新过的内容,内容也会被写线程及时更新。

Final变量

JSR133对final变量的规定是:

构造函数中的final成员变量的写入,和这个构造完毕的对象赋值给引用,这两个不会被重排序。即final的成员变量写入,肯定保证在此对象被其他人引用之前写入完毕。
初次读一个包含final成员变量的引用,于初次读这个final成员变量不可重新被排序。
构造函数中final指针指向的成员变量,也会先赋值完毕,才可以被引用。

Synchronize 锁

锁是广大程序员最熟悉的机制。利用锁可以很好避免优化等造成的不同步问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程 java