影响java线程安全的三个因素

java的线程安全性

线程安全性:当多个线程访问某个类时,不管运行时环境采用任何调度方式或者这些进程将如何交替执行,而且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

线程安全性主要体现在三个方面:

原子性:提供了互斥访问,同一时刻只能有一个线程对它进行操作

可见性:一个线程对主内存的修改可以及时的被其他线程观察到。

有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。

原子性

synchronized:不可中断锁,适合竞争不激烈,可读性好;在竞争激烈的适合,性能下降非常快。依赖jvm实现锁

Lock:可中断锁,多样化同步,竞争激烈时能维持常态。依赖特殊的CPU指令,代码实现,代表性ReentrantLock

Atomic:竞争激烈时能维持常态,比Lock性能好;只能同步一个值。juc里面的Atomic包都是通过cas来完成原子性的。

后面会专门总结一下java锁相关的内容。

java内存模型

当讨论到内存可见性问题应该第一步就是画出java内存模型然后在进行讨论。java内存模型可以参照这个java内存模型,重点是记住图就可以,其余的了解即可。

可见性

可见性:一个线程对主内存的修改可以及时的被其他线程观察到。

导致共享变量在线程间不可见的原因:

    1、线程交叉执行

    2、重排序结合线程交叉执行

    3、共享变量更新后的值没有在工作内存与主存间及时更新

解决可见性问题可以用synchronized和volatile关键字。注意volatile只能保证可见性,并不能保证原子性,同时也能够禁止重排序问题。而synchronized既可以保证原子性,也可以保证可见性。

volatile 性能:volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

可见性-synchronized

JMM关于synchronized的两天规定:
                1)线程解锁前,必须把共享变量的最新值刷新到主内存
                2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值
(注意,加锁与解锁是同一把锁)

可见性-volatile

通过加入内存屏障和禁止重排序优化来实现可见性。
                1)对volatile变量写操作时,会在写操作后加入一条store(StoreStore和StoreLoad)屏障指令,将本地内存中的共享变量值刷新到
主内存。如下图:


                2)对volatile变量读操作时,会在读操作后加入一条load(LoadLoad和LoadStore)屏障指令,从主内存中读取共享变量。如下图:

 

有序性

Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响
到多线程并发执行的正确性。

只会对不存在数据依赖性的进行重排序。

在java里面:volatile可以保证一定的有序性,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当
于是让线程顺序执行同步代码,自然就保证了有序性

另外java内存模型具备一些先天的有序性,不需要通过任何手段就能得到保证的有序性,通常称之为happens-befor原
则。

一个线程观察其他线程中的指令,指令顺序,由于指令重排序的存在,观察的结果一般都会杂乱无序。如果两个操作的执
行次序无法从happens-befor原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意的对他们进行重排序。

数据依赖性

如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分下列三种类型:

名称代码示例说明
写后读a = 1;b = a;写一个变量之后,再读这个位置。
写后写a = 1;a = 2;写一个变量之后,再写这个变量。
读后写a = b;b = 1;读一个变量之后,再写这个变量。

上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变。

编译器和处理器可能会对没有数据依赖性的操作做重排序。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。

不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。

有序性-happens-befor原则

程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。 注意:虚拟机只会对不存在数据依赖的指令进行重排序。这个原则只能保证单线程执行的有序性,无法保证在多线程的有序性。

锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作

volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作

传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生
于操作C。

线程启动规则:Thread对象的start()方法先行与发生线程的每一个动作

线程中断规则:对线程interrupt()方法的调用先行发生与被中断线程的代码检测到中断事件的发生

线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、
Thread.isAlive()的返回值手段检测到线程已经终止执行。

对象终止规则:一个对象的初始化完成先行发生于他的finalize()方法的开始。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值