线程的安全性分析



一、线程安全的本质

在这里插入图片描述

每个内核都有自己独立的缓存空间,缓存之间的数据是不可见的,只有当数据写入公共缓存区之后,数据才是可见的。导致多线程下,数据不一致。

1. 原子性

public class Demo {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        for (int j = 0; j < 1000; j++) {
            new Thread(()-> count++).start();
        }
        Thread.sleep(3000);
        System.out.println(count);
    }
}

正常结果count = 1000, 但是count++无法保证原子性,导致结果小于等于1000
在这里插入图片描述

2.可见性

在这里插入图片描述

3.有序性

在这里插入图片描述
在这里插入图片描述

二、Java内存模型

Java内存模型是一种抽象结构,它提供了合理的禁用缓存以及禁止指令重排序的方法来解决可见性、有序性问题。

在这里插入图片描述

1.JMM和硬件模型对应简图

在这里插入图片描述

2.可见性、有序性的解决方案

Volatile、synchronized、final关键字
Happens-Before原则

三、Synchronized的作用

可以解决原子性、可见性、有序性问题

1.锁的范围

1.对于普通同步方法,锁是当前实例对象
2.对于静态同步方法,锁是当前类的Class对象
3.对于同步代码块,锁是Synchronized括号里配置的对象

在这里插入图片描述

四、Volatile的作用

Volaile可以用来解决可见性和有序性问题

1. Lock指令的作用

将当前处理器缓存行的数据写回到系统内存。
写回内存操作会使在其他CPU里缓存了该内存地址的数据无效。

2. 什么时候用Volatile

当存在多个线程对同一个共享变量进行修改的时候。需要增加Volatile,保证数据修改的实时可见

3. CPU层面的内存屏障

Store Barrier: 强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行,并把store缓冲区的数据都刷到CPU缓存
Load Barrier:强制所有在load屏障指令之后的load指令,都在该load屏障指令执行之后被执行,并且一直等到load缓冲区被该CPU读完才能执行之后的load指令
Full Barrier:复合了load和store屏障的功能

4. 总结

  1. volatile 实际上是通过内存屏障防止指令重排序以及禁止cpu告诉缓存来解决可见性问题
  2. Lock指令,它本意上是禁止高速缓存解决可见性问题,但实际上在这里,它表示的是一种内存屏障的功能。也就是说针对当前的硬件环境,JMM层面采用Lock指令作为内存屏障来解决可见性问题。

五、final域

final 在Java中是一个保留关键,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用。

1. final域和线程安全的关系

对于final域,编译器和处理器要遵守两个重排序规则

  1. 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
  2. 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

2. 写final域重排序规则

  1. JMM禁止编译器把final域的写重排序到构造函数之外。
  2. 编译器会在final域的写之后,构造函数return之前,插入一个StoreStore屏障。这个屏障禁止处理器把final域的写重排序到构造函数之外。

在这里插入图片描述
在这里插入图片描述

3. 读final域重排序规则

在一个线程中,初次读对象引用与初次读改对象包含的final域,JMM禁止处理器重排序这两个操作,编译器会在读final域操作的前面插入一个LoadLoad屏障。
在这里插入图片描述
在这里插入图片描述

4. 溢出带来的重排序问题

在这里插入图片描述
在这里插入图片描述

六、Happers-Before规则

Happers-Before是一种可见性规则,它表达的含义是前面一个操作的结果对后续操作是可见的。

  1. 程序顺序规则
  2. 监视器锁规则
  3. Volatile变量规则
  4. 传递性规则
  5. start()规则
  6. Join()规则

1. 程序顺序规则

在单线程中,指令重排序,不会影响程序执行结果。

2. 监视器锁规则

对一个锁的解锁Happens-Before于后续对这个锁的加锁。(线程B读到的线程A解锁的变量,一定是线程A修改之后的值)

3. Volatile变量规则

对一个volatile域的写,Happens-Before于任意后续对这个volatile域的读。

4. 传递性规则

如果A Happends-Before B, 且B Happens-Before C,那么A Happens-Before C。

七、原子类Atomic

八、ThreadLocal作用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值