
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
软件编程开发安全问题一直以来都是需要程序员长期关注的一个问题,而本文我们就通过案例分析来了解和学习一下,Java线程安全问题都有哪些。
对于普通变量(指的是没有被voliate修饰的变量),假设有两条线程A和B并发执行,线程A和线程B同时将该变量从主内存读取到自己的工作内存,这时线程A和线程B获取到相同的初始数据。假设线程A先执行,该变量+1后被写回主内存。这个写回数据到主内存的动作线程B并不知道。
接下来线程B获得执行权,线程B对该变量+1后,再写回主内存。此时线程B其实就覆盖掉了线程A的操作,从而引发了线程安全问题。
如果变量加了voliate关键字,JMM会解决上述案例中线程A对该共享变量执行+1操作后的“线程B”并不知道的问题,voliate确保线程A对变量修改后,所有其他线程对该修改立即可见,也就是,线程B也知道了该变量的新值,从而可以在新值的基础上进行操作,也就避免了线程安全问题。
指令重排
这个问题比较简单,一般来讲,出于性能考虑,JVM并不是完全按照我们代码的顺序生成机器码的,他会判断在不影响程序逻辑的基础上调整我们代码的顺序,我们一般把这个顺序调整称为指令重排。
然而,指令重排虽然不会影响单线程应用的执行结果,但是在多线程并发环境下,指令重排有可能会导致线程安全问题。
voliate关键字会避免指令重排,因此,从指令重排的角度,可以避免线程安全问题。
voliate是否会彻底避免线程安全问题?
根据以上分析,我们猜测的答案应该是:voliate可以彻底避免线程安全问题。
然而,答案是:这个猜测是错误的。
这个答案很让人费解,但是这个答案是对的,你可以很容易的通过测试进行验证,但是解释起来却比较麻烦。
这又涉及到一个操作原子性的问题,原子性的操作一口气完成,不允许其他线程中断,而非原子性的操作却无法保证这一点,操作的过程中可能会被其他线程中断。
比如我们上面的例子,counter++的这个++操作,就不是原子性的,操作系统底层在执行++操作的时候先会将counter变量的值从内存(此时你可以理解为工作内存)读入到CPU寄存器,然后再进行+1操作,之后再从寄存器写回到工作内存。这3个步骤的任何一步都有可能被中断。
我们尝试举例解释一下volicate无法确保线程安全性的问题:counter是voliate变量,线程ABC并发,假设线程A先完成了counter++的操作,这个时候voliate确保该修改写回主内存后立即被线程BC获取到,这个时候线程安全问题没有发生,一切正常。此时假设线程BC并发执行,线程B的++操作被线程C的++操作中断,随后BC同时完成了++操作,当他们将操作后的counter值写回主内存时,线程安全问题发生!
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。更多内容请加danei456学习了解。欢迎关注“达内在线”参与分销,赚更多好礼。