我有一个thread1
:
if(object != null){
object.play();
}
另一个thread2
可以随时写入null
参考object
。
我将同时运行这些线程。我知道thread2
可以在检查后重写object
引用null
,这会抛出NullPointerException
. 检查后是否可以thread2
重写object
参考NullPointerException
?
我有一个thread1
:
if(object != null){
object.play();
}
另一个thread2
可以随时写入null
参考object
。
我将同时运行这些线程。我知道thread2
可以在检查后重写object
引用null
,这会抛出NullPointerException
. 检查后是否可以thread2
重写object
参考NullPointerException
?
在 NullPointerException 检查后是否可以让 thread2 重写对象引用?
绝对 - 它可以改变方法执行object
时的值play()
,如果这就是你的意思。这本身不会导致错误。
请注意,如果没有同步或其他内存屏障,线程 2 可能会在object
不确定的时间段内更改线程 1 的值而不会注意到。
如果不了解代码的更大目标,很难说你应该做什么。
简单的synchronized
例子:
/**
To maintain thread safety, only access this through getter and setter
or other synchronized method
**/
private ObjectType object;
public synchronized void setObject(ObjectType object) {
this.object = object;
}
public synchronized ObjectType getObject() {
return object;
}
public void doPlay() {
final ObjectType obj = getObject();
//here, thread 2 can change "object", but it's not going to affect this thread
//as we already safely got our reference to "object" in "obj".
if(obj != null){
obj.play();
}
}
public synchronized void alterativeDoPlay() {
//the difference here is that another thread won't be able to change "object"
//until the object's play() method has completed.
//depending on the code in play, this has potential for deadlocks, where as
//the other `doPlay` has zero deadlock potential.
if(object != null){
object.play();
}
}
您可以在此处使用CountDownLatch。其中 Thread1 将等待 Thread2 倒计时,您可以在 thread2 中执行任务并停止倒计时。
代码片段 -
CountDownLatch latch = new CountDownLatch(1);
new Thread1(latch).start();
new Thread2(latch).start();
public class Thread1 extends Thread {
private final CountDownLatch startLatch;
public Thread1(CountDownLatch startLatch) {
this.startLatch = startLatch;
}
public void run() {
try {
startLatch.await();
// ... perform task
} catch (InterruptedException iex) {}
}
}
public class Thread1 extends Thread {
private final CountDownLatch stopLatch;
public Thread1(CountDownLatch stopLatch) {
this.stopLatch = stopLatch;
}
public void run() {
try {
// perform task
} finally {
stopLatch.countDown();
}
}
}
如果object
是一个实例变量或一个可以从多个线程更改的静态变量,它的值可以在您在if
语句中测试它的时间和调用它的实例方法的时间之间发生变化。
您可以通过将对象复制到局部变量中来修改代码以避免此问题,如下所示:
Playable objectCopy = object;
if(objectCopy != null) {
objectCopy.play();
}
由于objectCopy
是一个局部变量,它的值不能在测试和调用之间改变play
。当然,可玩对象本身的状态是可以改变的,但这不是通过null
检查就能解决的。
根据布赖恩定律:
When we write a variable, which next has to be read by another thread, or when we are reading a variable which has lately been written by another thread, then use synchronization.
Synchronize the atomic statements or getter/setters which has access to the crucial state of data with the same monitor lock.
-使用synchronization
。
-你可以使用CountDownLatch
从java.util.concurrent
您将需要使用某种形式的同步原语来解决这个问题。请参阅此处的“同步语句” 。在您的情况下,您将需要包装整个if
块以及任何使用或更新的线程中的任何位置object2
。
正如我的教授所说:“并发是一个非常不稳定的人。我们永远不知道对他有什么期望。” 提出您的问题:
在 NullPointerException 检查之后,thread2 是否可以重写对象引用?
是的
在线程 1 出现 1 次期间,线程 2 可以多次访问该对象。或者反过来。thread1 可能出现多次,而 thread2 访问对象。
如果你使用简单
System.out.println();
在代码中的许多地方,您可能会注意到控制台中的输出显示在 NullPointerException 错误之后(如果没有被捕获)。