多线程之二:java内存模型和线程安全

整理自炼数成金

源码连接:

 

1.线程安全是什么:

 

    指某个函数、函数库在多线程环境中被调用时,能够正确地处理各个线程的局部变量,使程序功 能正确完成。

 

2.内存模型

    – 每一个线程有一个工作内存和主存独立

    – 工作内存存放主存中变量的值的拷贝

       

    当数据从主内存复制到工作存储时,必须出现两个动作:第一,由主内存执行的读(read)操作;第二,由工作内存执行的相应的load操作;当数据从工作内存拷贝到主内存时,也出现两个操作:第一个,由工作内存执行的存储(store)操作;第二,由主内存执行的相应的写(write)操作。

    对于普通变量,一个线程中更新的值,不能马上反应在其他变量中

    如果需要在其他线程中立即可见,需要使用 volatile 关键字

 

3.由于内存模型而引出线程安全的一些特性:

    – 原子性

        原子性是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就 不会被其它线程干扰。而且CPU执行指令是一条一条的,所以在应用里同时对一个对象进行读写,都不会影响一致性。

    – 可见性

        一个线程修改了变量,其他线程可以立即知道。

    – 保证可见性的方法

        – volatile

        – synchronized (unlock之前,写变量值回主存)

        – final(一旦初始化完成,其他线程就可见)

    – 有序性

    在本线程内,操作都是有序的

    在线程外观察,操作都是无序的。(指令重排 或 主内存同步延时)

指令重排

    - 指令重排是JVM对字节码执行的一个优化

    - 一条指令的执行是可以分为很多步骤的

        – 取指 IF

        – 译码和取寄存器操作数 ID

        – 执行或者有效地址计算 EX

        – 存储器访问 MEM

        – 写回 WB

    该语句需要9步才可以执行完,

         

 

    我们看下以下语句的指令正常的执行步骤

        a=b+c

        d=e-f

    从该图可以看出,这2条语句需要执行14步

         

 

    我们再看下通过指令重排的执行步骤

    从该图可以看出,重排后只需要12步就可以执行完

         

    - 指令重排的好处

        可以使流水线更加顺畅

    减轻处理压力,加快处理时间

 

    - 线程内串行语义

        • 写后读 a = 1;b = a; 写一个变量之后,再读这个位置。

        • 写后写 a = 1;a = 2; 写一个变量之后,再写这个变量。

        • 读后写 a = b;b = 1; 读一个变量之后,再写这个变量。

        • 以上语句不可重排

        • 编译器不考虑多线程间的语义

        • 可重排: a=1;b=2;

    -破坏线程间的有序性

[java] view plain copy

  1. class OrderExample {  
  2.   
  3. int a = 0;  
  4.   
  5. boolean flag = false;  
  6.   
  7.    
  8.   
  9. public void writer() {  
  10.   
  11. a = 1;                     
  12.   
  13. flag = true;             
  14.   
  15. }  
  16.   
  17.    
  18.   
  19. public void reader() {  
  20.   
  21. if (flag) {                  
  22.   
  23. int i =  a +1;        
  24.   
  25. ……  
  26.   
  27. }  
  28.   
  29. }  
  30.   
  31. }  

        线程A首先执行writer()方法

        线程B线程接着执行reader()方法

        线程B在int i=a+1 是不一定能看到a已经被赋值为1

        因为在writer中,两句话顺序可能打乱

    -保证有序性的方法

        

[java] view plain copy

  1. class OrderExample {  
  2.   
  3. int a = 0;  
  4.   
  5. boolean flag = false;  
  6.   
  7.    
  8.   
  9. public synchronized void writer() {  
  10.   
  11. a = 1;                     
  12.   
  13. flag = true;             
  14.   
  15. }  
  16.   
  17.    
  18.   
  19. public synchronized void reader() {  
  20.   
  21. if (flag) {                  
  22.   
  23. int i =  a +1;        
  24.   
  25. ……  
  26.   
  27. }  
  28.   
  29. }  
  30.   
  31. }  

 

        同步后,即使做了writer重排,因为互斥的缘故,reader 线程看writer线程也是顺序执行的。

 

    -指令重排的基本原则

        – 程序顺序原则:一个线程内保证语义的串行性

        – volatile规则:volatile变量的写,先发生于读

        – 锁规则:解锁(unlock)必然发生在随后的加锁(lock)前

        – 传递性:A先于B,B先于C 那么A必然先于C

        – 线程的start方法先于它的每一个动作

        – 线程的所有操作先于线程的终结(Thread.join())

        – 线程的中断(interrupt())先于被中断线程的代码

    对象的构造函数执行结束先于finalize()方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值