原子操作

  "原子操作(atomic operation)是不需要synchronized",这是Java多线程编程的老生常谈了。

  所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (cpu上下文切换)。

  定义:一个操作是原子的(atomic),如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体式原子性的核心。

  首先处理器会自动保证基本的内存操作的原子性。处理器保证从系统内存当中读取或者写入一个字节是原子的,意思是当一个处理器读取一个字节时,其他处理器不能访问这个字节的内存地址,也就是lock住这块内存,在我关于信号量的随笔中有提到《windows核心编程》,JVM同样也是。

  我们以decl (递减指令)为例,这是一个典型的"读-改-写"过程,涉及两次内存访问。设想在不同CPU运行的两个进程都在递减某个计数值,可能发生的情况是:

  ⒈ CPU A(CPU A上所运行的进程,以下同)从内存单元把当前计数值⑵装载进它的寄存器中;
  ⒉ CPU B从内存单元把当前计数值⑵装载进它的寄存器中。
  ⒊ CPU A在它的寄存器中将计数值递减为1;
  ⒋ CPU B在它的寄存器中将计数值递减为1;
  ⒌ CPU A把修改后的计数值⑴写回内存单元。
  ⒍ CPU B把修改后的计数值⑴写回内存单元。

  我们看到,内存里的计数值应该是0,然而它却是1。如果该计数值是一个共享资源的引用计数,每个进程都在递减后把该值与0进行比较,从而确定是否需要释放该共享资源。这时,两个进程都去掉了对该共享资源的引用,但没有一个进程能够释放它--两个进程都推断出:计数值是1,共享资源仍然在被使用。

  原子性不可能由软件单独保证--必须需要硬件的支持,因此是和架构相关的。在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。(我不是很懂)

  那么,C#中,当我们对一个基础值类型做改变的时候,等同于直接操作该值所存在的内存块。所以值类型的变量复制值就是copy一个内存值去开辟一个新内存块存进去。所以这里由于硬件的原子性支持,这种操作即便是在多线程之下也是安全的。然而,int64类型在32位intel处理器上的赋值操作会因为兼容性而被拆分成两步,在多线程的情况下,两步之间有可能被打断,所以不是线程安全的。

  也有人会说静态方法是否线程安全,其实,任何方法就像C里面的函数一样,都是线程安全的,只是一旦你在方法内部去声明一些静态变量或者用到全局变量,线程同步问题你就必须考虑了。

原文地址:https://www.cnblogs.com/Mushrooms/p/5127998.html