4

我有一个thread1

if(object != null){
   object.play();
}

另一个thread2可以随时写入null参考object

我将同时运行这些线程。我知道thread2可以在检查后重写object引用null,这会抛出NullPointerException. 检查后是否可以thread2重写object参考NullPointerException

4

7 回答 7

6

在 NullPointerException 检查后是否可以让 thread2 重写对象引用?

绝对 - 它可以改变方法执行object时的值play(),如果这就是你的意思。这本身不会导致错误。

请注意,如果没有同步或其他内存屏障,线程 2 可能会在object不确定的时间段内更改线程 1 的值而不会注意到。

如果不了解代码的更大目标,很难说你应该做什么。

于 2012-10-08T12:34:32.957 回答
5

简单的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(); 
  }
}
于 2012-10-08T12:54:24.907 回答
1

您可以在此处使用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();
    }
  }
}
于 2012-10-08T12:35:02.360 回答
1

如果object是一个实例变量或一个可以从多个线程更改的静态变量,它的值可以在您在if语句中测试它的时间和调用它的实例方法的时间之间发生变化。

您可以通过将对象复制到局部变量中来修改代码以避免此问题,如下所示:

Playable objectCopy = object;
if(objectCopy != null) {
    objectCopy.play();
}

由于objectCopy是一个局部变量,它的值不能在测试和调用之间改变play。当然,可玩对象本身的状态是可以改变的,但这不是通过null检查就能解决的。

于 2012-10-08T12:40:05.333 回答
1

根据布赖恩定律

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

-你可以使用CountDownLatchjava.util.concurrent

于 2012-10-08T12:45:10.747 回答
0

您将需要使用某种形式的同步原语来解决这个问题。请参阅此处的“同步语句” 。在您的情况下,您将需要包装整个if块以及任何使用或更新的线程中的任何位置object2

于 2012-10-08T12:37:04.173 回答
0

正如我的教授所说:“并发是一个非常不稳定的人。我们永远不知道对他有什么期望。” 提出您的问题:

在 NullPointerException 检查之后,thread2 是否可以重写对象引用?

是的

在线程 1 出现 1 次期间,线程 2 可以多次访问该对象。或者反过来。thread1 可能出现多次,而 thread2 访问对象。

如果你使用简单

System.out.println();

在代码中的许多地方,您可能会注意到控制台中的输出显示在 NullPointerException 错误之后(如果没有被捕获)。

于 2012-10-08T12:47:39.767 回答