Java内存模型

Java内存模型的抽象

在java中,所有实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享(本文使用“共享变量”这个术语代指实例域,静态域和数组元

素)。局部变量、方法定义参数、异常处理器参数不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。


所谓线程安全无非是要控制多个线程对某个资源的有序访问和修改。总结Java的内存模型,要解决两个主要问题:可见性和有序性


1. 可见性问题

多个线程之间是不能互相传递数据通信的,它们之间的沟通只能通过共享变量来进行。Java内存模型规定Jvm有主内存,主内存是多个线程共享的。当new一个对象的时候是分配在主内存中,每个线程都有自己的工作内存,工作内存存储了主内存中对象的副本


线程操作某个对象时,执行顺序如下:

1. 从主内存复制变量到当前工作内存(read and load)

2. 执行代码,改变共享变量值(use and assign)

3. 将工作内存数据刷新到主内存(store and write)


Jvm规范定义了线程对主内存的操作指令:read 、load 、 use 、 assign 、 store 、 write。当一个共享变量在多个线程的工作内存中都有副本时,如果一个线程修改了这个共享变量,那么其他线程如果没有看到这个被修改后的值,这就是多线程的可见性问题。


2. 有序性问题

假设共享变量x现在值为10,线程a加1,线程b减1,从表面上看,似乎最终x还是10,但是多线程情况下会有这种情况发生:

1:线程a从主内存读取x副本到工作内存,工作内存中x值为10

2:线程b从主存读取x副本到工作内存,工作内存中x值为10

3:线程a将工作内存中x加1,工作内存中x值为11

4:线程a将x提交主存中,主存中x为11

5:线程b将工作内存中x值减1,工作内存中x值为9

6:线程b将x提交到中主存中,主存中x为9 


Jvm在真正执行代码的时候并不一定是完全按照代码的真正顺序执行,这就是所谓的指令重排序

什么是指令重排序?一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序和代码中的顺序一致。

如:

int a = 10; // 语句1

int r = 2;  // 语句2

a = a + 3;  // 语句3

r = a * a;  // 语句4

这段代码有4个语句,那么可能的一个执行顺序是:

语句2 -> 语句1 ->  语句3 -> 语句4

指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。 


synchronized关键字

Java使用synchronized关键字作为多线程并发环境下的执行有序性的保证手段之一,当一段代码会修改共享变量,加了synchronized后这段代码就变成了互斥区

synchronized(锁){

        // 互斥区

}

理论上每个对象都可以作为锁,但一个对象作为锁时,应该被多个线程共享,这样才显得有意义,在并发环境下,一个没有共享的对象作为锁时没有意义的。下面代码中,lock变量作为锁根本没有意义,因为它根本不是共享对象,每个线程进来都会执行Object lock = new Object(); 每个线程都有自己的lock,根本不存在锁竞争。

  1. public class ThreadTest{  
  2.   public void test(){  
  3.      Object lock=new Object();  
  4.      synchronized (lock){  
  5.         //do something  
  6.      }  
  7.   }  
  8. }  
一个线程执行互斥区代码过程如下:

1 获得同步锁

2 清空工作内存

3 从主存拷贝变量副本到工作内存

4 对这些变量计算

5 将变量从工作内存写回到主存

6 释放锁


volalile关键字

volatile是java提供的一种同步手段,只不过它是轻量级的同步

1. 保证变量在各个线程的可见性,意思就是这个变量的值一修改,其他线程可以立刻得知。而一个普通变量需要先写回主内存,然后其他线程去读取这个值。

2. 禁止指令重排序优化,但不是完全严格的顺序

1
2
3
4
5
6
7
8
//x、y为非volatile变量
//flag为volatile变量
 
x =  2 ;         //语句1
y =  0 ;         //语句2
flag =  true ;   //语句3
x =  4 ;         //语句4
y = - 1 ;        //语句5

由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。

使用场景:状态标记量, 如果v不是volatile变量,那么编译器可以在writer线程中重排序写入操作,那么reader线程中的读取x变量的操作可能会看到0

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }

  public void reader() {
    if (v == true) {
      //uses x - guaranteed to see 42.
    }
  }
}

http://www.infoq.com/cn/articles/java-memory-model-1

http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html

http://hllvm.group.iteye.com/group/wiki/2877-synchronized-volatile

http://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字

http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值