Java多线程相关知识【10】--解释volatile关键字


菜鸟的学习笔记,欢迎大神指导批评。

1.问题的引入

​ 假设两个人同时对银行的一个账户进行存入1元钱操作,若银行未进行一定的保障,则有可能造成两个人同时存入一元钱,而这个账户仅仅会添加一元钱,而将会丢失一元钱的情况,那么如何保证这种情况不被发生呢?

2.问题发生的原因

​ 由于两人同时存入前,计算机读取数据时,并不会判断在读取数据后是否会发成改变,并且都将相应读到的数据存到计算机内,而仅对当前读到的数据进行保存,而忽略了多个人同时操作时发生的与时间有关的问题,故会造成读到脏数据的情况。(详见操作系统,多线程相关原理的解释)

3.解决的方法

3.1.解决方法的理论

3.1.1总线加锁

​ 总线加锁即当操作共享数据时,将会为访问的数据进行全部加锁的方式,这种方式虽然能解决相关程序的运行问题,但是,同样也会造成系统的运行效率低下等问题。

3.1.2.使用cpu高速缓存一致性协议(Intel 提出)

思想:cpu写入数据时,发现被共享时,会通知其他拥有副本的cpu需要立即更新缓存,即之前的信息失效,其他的cpu在收到信号后,会重新到相应内存区重新获取相应数据,这也就解决多个线程访问时数据不一致的问题。而Java在使用volatile关键字时,即使用了这个设计的思想。

3.2.Java中的多线程的三大原则

3.2.1.原子性(atomic)

​ 一个操作要么都成功,要么都失败,不能因为任何原因被中断。

3.2.2.有序性(order)

相应的操作需要按顺序执行(重排序为只要求结果一致性(重排序能优化单线程的执行效率),但在多线程中,会产生相关的错误)

Java默认提供的有序性(happens-before)
  1. 代码执行时顺序为写在前面的代码先发生,写在后面的后发生。

  2. 解锁操作必须在锁之后。

  3. 多线程时,volatile修饰的关键字先执行写操作,后执行读操作。

  4. 如果A操作先于B操作,B操作先于C操作,那么A操作必定先于C操作。

  5. 在线程启动时,线程的创建必定优先于线程的执行。

  6. 在线程中断时,线程的中断操作(interrupt)必须发生在捕获异常发生之前。

  7. 对象的初始化,必须发生在销毁之前。

  8. 线程的所有操作必须发生在线程的销毁之前。

3.3.3.可见性(visable)

任何时间看到的数据应为最新的数据,而并非为旧的数据(防止操作系统的与时间有关的错误)

3.3.Java中的解决方法(使用volatile)

在Java中,未了解决操作系统中的与时间有关的错误的问题时,为了简单起见,通常会使用volatile关键字,但本关键字也有一定的使用限制,具体如下:

3.3.1.使用volatile关键字的优点和缺点
  1. 一旦此变量被关键字修饰,则虚拟机则保证了执行的可见性(V)和有序性(O),但并不能保证这个变量的原子性(A)。
  2. 保证重排序时,指令的执行顺序不被修改
  3. 强制使缓存的数据立刻写入主存
  4. 若对关键字修饰的变量进行写操作,则会是其他cpu的缓存的数据在写入修改后立即失效。
3.3.2.volatile关键字的使用场景
  1. 需要进行状态量的标记时,可使用本关键字。
  2. 需要保证关键字修饰的语句的执行的先后顺序不发送改变,即运行一致性时,可使用本关键字。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值