Java 多线程:对象在使用时被设置为null
我有一个小应用程序,它有一个渲染线程。该线程所做的就是在对象的当前位置绘制对象 我有一些代码,比如:Java 多线程:对象在使用时被设置为null,java,multithreading,nullpointerexception,Java,Multithreading,Nullpointerexception,我有一个小应用程序,它有一个渲染线程。该线程所做的就是在对象的当前位置绘制对象 我有一些代码,比如: public void render() { // ... rendering various objects if (mouseBall != null) mouseBall.draw() } 然后,我还有一些鼠标处理程序,当用户单击鼠标时,可以创建并将鼠标球设置为新球。然后,用户可以拖动鼠标,球将跟随鼠标移动的方向。当用户释放球时,我有另一个鼠标事件,它将mouseBa
public void render()
{
// ... rendering various objects
if (mouseBall != null) mouseBall.draw()
}
然后,我还有一些鼠标处理程序,当用户单击鼠标时,可以创建并将鼠标球设置为新球。然后,用户可以拖动鼠标,球将跟随鼠标移动的方向。当用户释放球时,我有另一个鼠标事件,它将mouseBall设置为null
问题是,我的渲染循环运行得足够快,在随机时间条件(mouseBall!=null)将返回true,但在该时间点后的那一瞬间,用户将放开鼠标,我将得到一个null指针异常,因为我尝试对null对象执行.draw()
这样一个问题的解决方案是什么?问题在于,您要访问
鼠标球两次,一次检查它是否为空,另一次调用它的函数。您可以使用以下临时方法来避免此问题:
public void render()
{
// ... rendering various objects
tmpBall = mouseBall;
if (tmpBall != null) tmpBall.draw();
}
您必须同步if和draw语句,以保证它们作为一个原子序列运行。在java中,可以这样做:
public void render()
{
// ... rendering various objects
synchronized(this) {
if (mouseBall != null) mouseBall .draw();
}
}
我知道您已经接受了其他答案,但第三种选择是使用java.util.concurrent.atomic包的AtomicReference类。这提供了检索、更新和比较操作,这些操作以原子方式进行,无需任何支持代码。在你的例子中:
public void render()
{
AtomicReference<MouseBallClass> mouseBall = ...;
// ... rendering various objects
MouseBall tmpBall = mouseBall.get();
if (tmpBall != null) tmpBall.draw();
}
因为这是一个原子操作,所以如果一个线程“看到”该值为null,它也会在其他线程有机会读取它之前将其设置为possibleNewBall引用
原子引用的另一个好习惯用法是,如果您正在无条件地设置某些内容,但需要使用旧值执行某种清理。在这种情况下,你可以说:
MouseBall oldBall = mouseBall.getAndSet(newMouseBall);
// Cleanup code using oldBall
原子整数有这些好处和更多;getAndIncrement()方法对于全局共享计数器非常有用,因为您可以保证对它的每次调用都将返回一个不同的值,而不考虑线程的交错。螺纹安全,无需担心。完全正确。如果要在没有同步块的情况下执行此操作,请确保mouseBall是易失性的。这会在mouseBall.draw()方法调用的整个过程中一直保持对“this”的锁定,这可能比保持锁定所需的时间要长。此外,只有当对鼠标球的所有其他访问(读写)也被同步(this){…}块包围时,这才是原子的。
MouseBall oldBall = mouseBall.getAndSet(newMouseBall);
// Cleanup code using oldBall