zoukankan      html  css  js  c++  java
  • 从汇编去分析线程安全

    首先要知道什么是线程安全?

      当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

    典型线程不安全的列子:

     1 import java.util.*;
     4 
     5 class Worker implements Runnable {
     6     private UnsafeCount unsafeCount;
     7 
     8     public Worker(UnsafeCount unsafeCount) {
     9         this.unsafeCount = unsafeCount;
    10     }
    11 
    12     @Override
    13     public void run() {
    14         // TODO Auto-generated method stub
    15         for (int i = 0; i < 1000; i++)
    16             unsafeCount.increase();
    17     }
    18 
    19 }
    20 
    21 public class UnsafeCount {
    22     private int count = 0;
    23 
    24     public void increase() {
    25         count++;
    26     }
    27 
    28     public int getCount() {
    29         return count;
    30     }
    31 
    32     public static void main(String[] args) throws InterruptedException {
    33         UnsafeCount uc = new UnsafeCount();
    34         
    35         //这里用了list简陋的方式控制线程的结束,更好的实现是用闭锁CountDownLatch或者栅栏CyclicBarrier
    36         List<Thread> list = new ArrayList<Thread>();//
    37         
    38         for (int i = 0; i < 10; i++) {
    39             Thread worker = new Thread(new Worker(uc));
    40             worker.start();
    41             list.add(worker);
    42         }
    43 
    44         //阻塞直到线程结束
    45         for (Thread t : list) {
    46             t.join();
    47         }
    48 
    49         System.out.println("total is: " + uc.getCount());
    50 
    51     }
    52 }

    运行结果(每次结果都不一样):
    total is: 7628

    我们来仔细分析一下这个结果,开启10个线程运行,每个线程都对count进行了1000次自增操作,期望的结果应该是1000*10=10000。很明显运行结果与期望结果不一致。结论是这个类是线程不安全的。为什么会出现这种情况了?

    原因是count++这个操作不是原子性,其实这个自增操作是个复合操作:读-改-写。 如果我们了解汇编语言的话,对应自增操作的汇编程序可能是:

    movl count, %eax  #将count的值读入eax的寄存器中,
    inc %eax  #寄存器eax里的值加1,即改写count值
    movl %eax, %ebx  #这里ebx寄存器存存放着count的内存地址,这里是值将改写的count值写入到内存中


    那么这样就存在一个问题,假如就存在2个线程A和B操作变量count,初始化时刻count为0. 在线程A未写入改写值之前,比如在A线程执行步骤2的时刻, 线程B开始执行,如下所示:

    线程A读入count值为0 (步骤1)  -》  改写count值为1(步骤2)  -》 将改写后的count值写入内存中(步骤3) 

                         线程A读入count值为0 (步骤1)  -》  改写count值为1(步骤2)  -》 将改写后的count值写入内存中(步骤3)

    因为线程A还没有更新改写count的值到内存,这时线程B读入count的值仍旧是0,导致最后2个线程结束后count的值为1。由此可见做了2次自增的操作,期望结果是2,但实际结果可能是1.这也是线程不安全的情况下,自增的操作的实际结果往往比期望结果小的原因。

    下篇准备将讲什么情况是线程不安全的。

      

  • 相关阅读:
    Python 缓冲区
    Python接收执行参数
    Python编码
    Maven 多环境 打包
    JS 时间 获取 当天,昨日,本周,上周,本月,上月
    Window Mysql 5.7.18安装
    Eclipse 更改Maven项目名
    Redis 命令
    Redis 安装 和 启动
    Mongodb 安装 和 启动
  • 原文地址:https://www.cnblogs.com/csu_xajy/p/4237489.html
Copyright © 2011-2022 走看看