线程安全

线程安全

什么是线程安全
程序运行的结果100%符合预期,不会出现有时正确,有时错误。 ——消除随机性并且正确。

什么时候会出现线程不安全
1.看线程之间是否有共享的数据的。
2.有线程修改共享数据。
上述两点都满足的话,则会出现线程不安全。

换言之,1.线程之间不存在共享的,天生是线程安全的。
2,即使线程之间有数据共享,但没有线程修改共享数据,则天生是线程安全的。

JVM运行时内存区域中,哪些位置是共享的?哪些是线程内部私有的?
1.nextPC值(PC区域)—— 私有的
2.栈区(java栈/native栈)——私有的
3.堆区 —— 共享的
4.方法区 —— 共享的
5.运行时常量池 —— 共享的

什么时候需要特别考虑线程安全问题
线程之间有修改静态属性或同一对象属性时,需要特别考虑线程安全问题,否则,一般不需要考虑。

局部变量是线程私有的数据,不会共享,不需要特别考虑线程安全问题。
属性/对象 + 静态属性/类 ,是共享的,需要考虑线程安全问题。

原子性、代码可见性、代码重排序
线程之间是以共享数据进行通信的,就需要特别关注这三点:原子性、代码可见性、代码重排序。

原子性
原子性是指一组不能再分割的操作。

例如:
float f = 1.0F —— 具备原子性
double a = 1.0 ——不具备原子性
(注意:JVM在设计时,是按照32bit为最小操作单位设计的)
double类型是64bit的,double a = 1.0 可被分为高32bit赋值 和 低32bit赋值,是两步操作,所以无法保证它的原子性。

int a = 9;
int b = a; ——也不是原子性的。它有两步操作:1.先从内存中读取a的值 2.把寄存器中的值
保存到b的内存中。是两步操作。

final int a = 7;
int b;
b= a; ——具备原子性。因为a是常量,就不需要读取a这步操作。

代码可见性
代码可见性指一个线程修改共享变量时,另一个线程是否能看到的问题。
在这里插入图片描述
java内存模型规定,所有的线程都有自己的工作内存。线程需要操作任何数据都需要把数据从主内存中加载到工作内存中,线程就在自己的工作内存中操作,在合适的机会,再把计算结果同步到主内存中。如果线程操作变量后没有将结果同步到主内存,那么操作对其他线程是不可见的。如果一个变量没有读取到新的值而是使用旧的值,也是不可见的。

代码重排序
定义:程序执行顺序和语句顺序不一致,这时,便发生了代码重排序。
作用: 重排序后执行效率更高。
谁来做代码重排序: 编译器 和 处理器。
特征:
1.单线程情况下,代码重排序后的结果和重排序之前效果一致。
2.多线程情况下,重排序后结果和重排序前结果不一致。
要求 JVM规定:重排序必须遵守happend-before 原则——规定哪些语句必须哪些语句之前。

要防止重排序,需要使用volatile关键字,它可以保证操作不会被重排序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值