APP下载

干货 了解java执行绪安全与共享资源

消息来源:baojiabao.com 作者: 发布时间:2024-05-18

报价宝综合消息干货 了解java执行绪安全与共享资源

允许被多个执行绪同时执行的程式码称作执行绪安全的程式码。执行绪安全的程式码不包含竞态条件。当多个执行绪同时更新共享资源时会引发竞态条件。因此,了解Java执行绪执行时共享了什么资源很重要。

区域性变数

区域性变数储存线上程自己的栈中。也就是说,区域性变数永远也不会被多个执行绪共享。所以,基础型别的区域性变数是执行绪安全的。下面是基础型别的区域性变数的一个例子:

区域性的物件引用

物件的区域性引用和基础型别的区域性变数不太一样。尽管引用本身没有被共享,但引用所指的物件并没有储存线上程的栈内。所有的物件都存在共享堆中。如果在某个方法中建立的物件不会逃逸出(译者注:即该物件不会被其它方法获得,也不会被非区域性变数引用到)该方法,那么它就是执行绪安全的。实际上,哪怕将这个物件作为引数传给其它方法,只要别的执行绪获取不到这个物件,那它仍是执行绪安全的。下面是一个执行绪安全的区域性引用样例:

样例中LocalObject物件没有被方法返回,也没有被传递给someMethod()方法外的物件。每个执行someMethod()的执行绪都会建立自己的LocalObject物件,并赋值给localObject引用。因此,这里的LocalObject是执行绪安全的。事实上,整个someMethod()都是执行绪安全的。即使将LocalObject作为引数传给同一个类的其它方法或其它类的方法时,它仍然是执行绪安全的。当然,如果LocalObject通过某些方法被传给了别的执行绪,那它就不再是执行绪安全的了。

物件成员

物件成员储存在堆上。如果两个执行绪同时更新同一个物件的同一个成员,那这个程式码就不是执行绪安全的。下面是一个样例:

如果两个执行绪同时呼叫同一个NotThreadSafe例项上的add()方法,就会有竞态条件问题。例如

注意两个MyRunnable共享了同一个NotThreadSafe物件。因此,当它们呼叫add()方法时会造成竞态条件。

当然,如果这两个执行绪在不同的NotThreadSafe例项上呼叫call()方法,就不会导致竞态条件。下面是稍微修改后的例子:

现在两个执行绪都有自己单独的NotThreadSafe物件,呼叫add()方法时就会互不干扰,再也不会有竞态条件问题了。所以非执行绪安全的物件仍可以通过某种方式来消除竞态条件。

执行绪控制逃逸规则

执行绪控制逃逸规则可以帮助你判断程式码中对某些资源的访问是否是执行绪安全的。

如果一个资源的建立,使用,销毁都在同一个执行绪内完成,

且永远不会脱离该执行绪的控制,则该资源的使用就是执行绪安全的。

资源可以是物件,阵列,档案,数据库连线,套接字等等。Java中你无需主动销毁物件,所以“销毁”指不再有引用指向物件。

即使物件本身执行绪安全,但如果该物件中包含其他资源(档案,数据库连线),整个应用也许就不再是执行绪安全的了。比如2个执行绪都建立了各自的数据库连线,每个连线自身是执行绪安全的,但它们所连线到的同一个数据库也许不是执行绪安全的。比如,2个执行绪执行如下程式码:

检查记录X是否存在,如果不存在,插入X

如果两个执行绪同时执行,而且碰巧检查的是同一个记录,那么两个执行绪最终可能都插入了记录:

执行绪1检查记录X是否存在。检查结果:不存在

执行绪2检查记录X是否存在。检查结果:不存在

执行绪1插入记录X

执行绪2插入记录X

同样的问题也会发生在档案或其他共享资源上。因此,区分某个执行绪控制的物件是资源本身,还是仅仅到某个资源的引用很重要。

2019-10-31 13:57:00

相关文章