线程安全

线程安全问题

即多线程并发出现稀奇古怪无法理解的问题。

如何解决线程安全问题?

只要并发满足如下性质即可

可见性

即一个线程对一个变量修改后其余所有线程能立即知晓这个变量被修改了。

这句描述可能很奇怪?难道我一个线程修改了一个变量,其他线程难道还不知道吗?

不妨运行一下这个代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Visibility {

static boolean flag = true;

public static void main(String[] args) throws InterruptedException {

new Thread(()->{
while (flag);
}).start();

Thread.sleep(1000);
flag = false;

}

}

如果真的满足可见性,那么程序就立即退出了。如果不满足,那么程序就会一直运行下去。

运行结果是程序一直处于运行状态,停不下来了。

为什么?我们不妨从底层了解一下。

当下CPU的处理速度是非常迅速的,高速的处理速度和低速的内存,严重不匹配的速率会极大的拖慢CPU的利用率,造成内存资源的浪费。

CPU的设计者肯定考虑到了这个问题,所以肯定是有解决方案的!!

解决方案就是通过增加缓存。

image-20230126130231130

CPU在进行指令操作的时候必然是需要数据的,数据首先会从主存拷贝到缓存,然后由缓存拷贝到CPU寄存器内进行计算。

关于高速缓存,量比较小,速度和CPU处理速度相当。

而且可能不止有一层。

image-20230126130602513

这个和线程的可见性有什么关系?

有关系有很大的关系。

在多核条件下,只要存在缓存在不进行特殊处理的情况下就可能出现缓存的不一致。对于同一变量的缓存千差万别。

CPU的缓存不一致如何影响到线程呢?

线程的本质是什么?CPU的一段时间片。如果CPU存在缓存不一致,那么线程是否也存在呢?答案是一定的。

我们知道java的多并发是安全的,然而CPU底层的并发是不具备可见性的,所以java的设计师一拍脑袋。这样下去不行啊,缓存不一致会导致java无法在服务端占据市场,得解决才行。所以就搞了一个JMM,解决了java的内存不一致问题。JMM于其说是一种技术不如说是一种规范,即你遵循我的规划,那么编译出来的class字节码在jvm上多线程并发是满足一致性的,当然你不遵循,那不好意思,我不确保。

关于其延展的内容不在讲解,太多了,jmm,as-if-serial,happens before,MESI。

有序性

java在有jit技术即会对频繁调用的代码进行jit编译,jit的过程中一些指令并具有依赖关系,可能会进行重排序,来优化代码的执行效率,然而优化的代码在单线程不会有任何问题,多线程就说不定了。

原子性

即一次操作要么全部执行完成,要么就不执行,不存在执行一半的情况。它表示了,要么操作是不可分割的,最小的操作。