美文网首页
并发程序出现诡异问题的常见根源

并发程序出现诡异问题的常见根源

作者: GuangHui | 来源:发表于2020-09-29 11:15 被阅读0次

    极客时间《Java并发编程实战》学习笔记1

    并发程序出现诡异问题的常见根源:缓存导致的可见性问题,线程切换带来的原子性问题,编译优化带来的有序性问题。

    1.缓存导致的可见性问题。

    什么是可见性呢,就是说当一个线程对共享变量进行了修改,另外一个线程能够立刻看到,我们就称为具有可见性。

    而多核时代,每个CPU都有自己的缓存,当多个线程在不同的cpu上执行时,这些线程操作的是不同的cpu缓存,此时一个线程对变量的操作,对于另外一个cpu上线程而言就不具备可见性了。

    2.线程切换带来的原子性问题。

    一个进程创建的所有线程,都是共享同一个内存空间的,所以如果线程做任务切换的话,成本就很低。也正是基于此,现代的操作系统都是基于轻量的线程来做调度,我们常提到的“任务切换”也都是指“线程切换”。

    java并发程序都是基于多线程的,自然也会涉及任务切换。而对于多线程的任务切换,任务时间片切换结果的时机,不是高级语言中编写的一条语句,而是CPU的指令。比如代码count += 1,其实至少是需要三条cpu指令。

    • 指令1:首先,需要把变量count从内存加载到CPU的寄存器;
    • 指令2:之后,在寄存器中执行+1操作;
    • 指令3:最后,将结果写入内存(缓存机制导致可能写入的是CPU缓存而不是内存)。

    任务切换可能发生在任何一条cpu指令执行完时,这样当多线程执行对count变量循环加1,得到的结果就很难得到顺序执行得到的结果。

    我们把一个或多个操作在cpu执行的过程中不被中断的特性就成为原子性。CPU能保证的原子操作是CPU指令级别的,而不是高级语言的操作符,这是违背我们直觉的地方。因此,很多时候我们需要在高级语言层面保证操作的原子性。

    3.编译优化带来的有序性问题

    有序性就是指程序按照代码的先后顺序进行执行。可编译器为了优化性能,有时候会改变程序中语句的先后顺序。而先后先后循序的调整,有时候就会带来一些意想不到的bug。

    总结:

    缓存导致的可见性问题,线程切换带来的原子性问题,编译优化带来的有序性问题,都是技术在解决一个问题的同时,带来的另一个问题。而这其实也是一种必然,就跟是药三分毒一个道理。关键是我们要在采用一项技术的同时,清楚它带来的问题是什么,以及如何规避。

    相关文章

      网友评论

          本文标题:并发程序出现诡异问题的常见根源

          本文链接:https://www.haomeiwen.com/subject/caasuktx.html