同步对象设置为null。

30

我有两个线程Thread1Thread2

//Within Thread1     
synchronized(obj1)  
{  
    obj1 = null;  
}  

//Within Thread2  
synchronized(obj1)  
{  
    do something  
}   

如果JVM首先执行线程1并将obj1设置为null,那么线程2会立即看到这个更改吗?还是需要时间,因为obj1尚未为null,JVM仍然可以运行线程2的同步块?


7
他无法自己尝试,任何与并发相关的问题都是不确定性的。在没有上下文的情况下,这些评论非常令人沮丧。 - John Vint
5个回答

41

这几乎肯定会破坏同步抽象 - 我不能保证 thread2 立即看到更改。 您永远不应更改正在同步的对象的引用,更不用将其设置为 null,这将导致任何进一步尝试对其进行同步时出现 NullPointerException


16
首先,让我强调一下,修改用于同步的变量是非常糟糕的。如果obj1被用作监视器,它应该是final且永远不应被更改。
话虽如此,回到你的问题:
如果JVM首先执行Thread1,则会在obj1上进行同步,将其设置为null,然后线程退出。第二个线程想要在obj1上同步,会抛出NullPointerException,因为在同步块中修改了obj1,保证Thread2能看到更新后的值(所以:NullPointerException是有保证的)。
如果在线程1获取obj1锁之后但在清除引用之前被中断,则线程2将锁定obj1并等待Thread1完成。然后它将成功进入监视器,因为之前由obj1引用的对象仍然存在。

由于 obj1 在同步条件中被使用而不是在同步块中,因此两个线程尝试同时进行同步并且 thread1 先进入,thread2 缓存了 obj1 的值,这种情况是否可能发生。当 thread1 退出时,thread2 从缓存中获取 obj1 的值并进入同步块,在同步块中看到更改。 - vjk
@Tomsaz 我没明白你的这句话:如果Thread1在获得obj1锁后但在清除引用之前被中断,Thread2会锁定obj1并等待Thread1完成。 首先,我们只有在线程被阻塞时才会中断它们,而且如果它们没有被阻塞,则中断它们没有任何效果,对吧? 基本上我从来没有看到过Thread1在完成同步块或在等待()在该锁本身上调用之前放弃锁的情况。 也就是说,Thread2什么时候会获得锁,为什么会等到Thread1完成呢? - sactiw
NullPointerException是*不被保证的。在synchronized块中执行写操作只能保证后续进入相同对象的synchronized块的线程可见性,但不能保证在进入synchronized块之前发生的读取。因此,在没有其他同步的情况下,第二个线程可能会抛出NullPointerException,也可能不会,这是不可预测的。只有在synchronized块内再次取消引用变量时,才能保证NullPointerException的发生。 - Holger

5

synchronized关键字锁定的是对象,而不是引用。如果将obj1(一个引用)设置为null,那么线程2无法锁定以前由obj1指向的对象,你会得到一个NullPointerException异常。


2
一个快速的解决方法是将对象变成一个只有一个元素的简单数组,并引用该数组进行同步,例如:

Object [] obj1 = {null};

该元素可以为null,不影响数组的存在。尽管这仍然违反了不使用对象本身进行同步的“规则”,但除非您的代码在其他地方使问题复杂化,否则这个快速修复应该能够按预期工作。

1
更改是立即生效的。当线程1“拥有”锁时,它可以随意更改obj1的值。线程2必须等待直到线程1释放锁。它一定会看到 obj1 == null。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接