并发理论总结1-并发源头和解决并发问题的方案

男娘i 2021-09-18 07:40 387阅读 0赞

并发bug源头

可见性,原子性,有序性。

可见性

问题本源:缓存

多核时代,每个CPU都有自己的缓存,当多个线程在不同的CPU上执行时,这些线程的数据存在不同的CPU缓存中,这也就导致了并发的可见性问题。

原子性

问题本源:线程切换 或者说 cpu的原子性是cpu指令。

JAVA的原子性是指一个线程在执行过程中不被中断,但是CPU的原子性是cpu指令,所以像JAVA这种高级语言的执行实际在CPU中执行,不光是从上往下执行,还会发生线程切换。

有序性

问题本源:JVM编译优化。

JVM编译字节码文件的时候,会优化性能的,有时候就会改变程序中语句的执行顺序。

解决可见性和有序性

粗暴的解决:直接禁用CPU缓存和编译优化。
优雅的解决:按需禁用,最大程度的保持代码的性能优异性。

解决可见性

JVM内存模型对可见性提出了解决的理论依据

Happends-Before规则

这个规则的核心是:前面操作的结果对于后续操作时可见的。
1.程序的顺序性规则
同一个线程中,程序按照自上而下的顺序执行,程序前面某个变量的修改对于后续操作时可见的。
2.volatile变量规则
对一个 volatile 变量的写操作, Happens-Before 于后续对这个 volatile 变量的读操作。
notice:
volatile 关键字并不是 Java 语言的特产,古老的 C 语言里也有,它最原始的意义就是禁用 CPU 缓存。告诉编译器,对这个变量的读写,不能使用 CPU 缓存,必须从内存中读取或者写入。
3.传递性规则
如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。
4.管程中的锁规则
对一个锁的解锁 Happens-Before 于后续对这个锁的加锁。
notice:
管程是一种通用的同步原语,在 Java 中指的就是 synchronized,synchronized 是 Java 里对管程的实现。
5.线程start()规则
主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作。
6.线程join()规则
主线程 A 等待子线程 B 完成(主线程 A 通过调用子线程 B 的 join() 方法实现),当子线程 B 完成后(主线程 A 中 join() 方法返回),主线程能够看到子线程的操作。所谓的“看到”,指的是对共享变量的操作。

以上是happens-before规则中常用到的规则。
实际应用中的解决方案
实际中更多地是用锁保护共享变量,来禁止CPU缓存,来解决可见性,切符合happens-before规则,释放锁对于获取锁是可见的。简单粗暴又好用。

解决有序性

通过volitale修饰变量,禁止jvm编译优化。
volatile boolean v = false;
notice:
和volitale相对应的是final,final修饰变量就是告诉编译器,这个变量生儿不变,可劲优化。

解决原子性

禁止线程切换,通过互斥锁来实现,保证在CPU中同一时刻只有一个线程对共享变量进行操作。

发表评论

表情:
评论列表 (有 0 条评论,387人围观)

还没有评论,来说两句吧...

相关阅读

    相关 并发编程Bug源头

    本文来自《Java并发编程实战》的第一篇《可见性、原子性和有序性问题:并发编程Bug的源头》,主要介绍了并发问题出现的原因。更多文章查看:[Java并发学习记录总目录][Jav

    相关 java并发编程并发解决方案

    一、并发和高并发是什么 并发:指同时拥有两个或多个线程;如果程序在单核处理运行,多个线程将交替换入或者换出内存,这些线程是同时存在的,每个线程都处于执行过程中的某个状态;如

    相关 Java并发理论总结

    多线程中稍微不注意就会出现线程安全问题,那么什么是线程安全问题?为什么会出现线程安全问题?出现线程安全的问题一般是因为**主内存和工作内存数据不一致性**和**重排序**...