0

我想要一些帮助来理解如何将对象设置为 null 在 java 中的工作原理。我有一种情况,乍一看似乎设置为 null 的对象突然不为 null,但显然情况并非如此。

我有一个类,我在其中创建一个对象。这个对象是一个场景。这是一个 Open GL ES 2.0 项目,因此该场景的 render() 和 updateLogic() 方法是从 onDrawFrame 调用的(这是通过场景管理器控制的,因此我们可以轻松切换场景)。

所以,我可能会有这样的事情(出于问题的目的,代码被删减):

public class MyGLRenderer implements GLSurfaceView.Renderer{

    MyScene myScene;
    SomeOtherScene someOtherScene;

    public void createScenes(){
        myScene = new MyScene(this);
        someOtherScene = new SomeOtherScene(this);
        SceneManager.getInstance().setCurrentScene(myScene);
    }

    public void cleanUp(){
        myScene = null;
        Log.v("tag","myScene (from MyGLRenderer) is: "+myScene);
        SceneManager.getInstance().setCurrentScene(someOtherScene);   //Scene changed but this won't take effect until the next 'tick'   
    } 

    @Override
    public void onDrawFrame(GL10 gl) {
        SceneManager.getInstance().getCurrentScene().updateLogic();
        SceneManager.getInstance().getCurrentScene().render();
    }

}

在上述情况下,处理被移交给myScene,看起来像这样:

public class MyScene implements Scene{

    MyGLRenderer renderer;

    public myScene(MyGLRenderer renderer){     
        this.renderer = renderer;
    }

    @Override
    public void render(){
        //Render something here
    }

    @Override
    public void updateLogic(){
        doSomething();           
        //The condition here could be anything - maybe the user taps the sceen and a flag is set in onTouchEvent for example
        if (someConditionIsMet){
            renderer.cleanup();
        }            
         Log.v("tag","myScene (from within myScene) is: "+this);
    }
}

因此,当我使用我的场景管理器设置场景时,处理将移交给该场景,并且它的 updateLogic 和渲染方法会从 onDrawFrame 连续调用。

当我运行我的代码时,我很惊讶它没有因 NullpointerException 而崩溃。日志是这样的:

myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from MyGLRenderer) is: null
myScene (from within myScene) is: com.program.name.MyScene@26354632

如您所见,“myScene”在调用 cleanUp() 方法并将其设置为 null 之前一直有效。但是代码然后返回到 myScene 以完成,它仍然有效(不为空)。

我真的很想了解 Java 中的事情是如何工作的——为什么它在一分钟内(或从一个地方)似乎是空的,然后又不是(从另一个地方)?

4

2 回答 2

1

您只需清除渲染器中对 MyScene 的引用,但该对象仍然存在并且 SceneManager 可能仍然保留它。

混乱似乎是关于“将对象设置为空”。那是不可能的。您只能将指向对象的变量设置为 null。在此之后,变量指向 null,但对象可能仍然存在(如您的情况)。

具体来说,您在这里将其交给场景管理器:

SceneManager.getInstance().setCurrentScene(myScene);

只有当没有任何东西持有对它的引用时,该对象才会被垃圾收集。在您的情况下,这可能是场景更改生效的时间。

于 2015-09-07T00:30:24.513 回答
0

看起来您遇到了线程安全错误。

除非您“安全地发布”值的更改,否则其他线程可以看到变量的陈旧值。(您有一个 CPU 更改了其缓存行中的值,但另一个 CPU 仍然看到过时的缓存数据 - 您需要强制缓存写入主内存,但这很昂贵,因此 Java 在被告知之前不会这样做)。

根据您的具体要求,有多种方法可以安全地发布值。查看您的代码,您似乎需要做的就是将 myScene 字段声明为易失性。它可能也应该是私有的。所以试试:

private volatile Scene myScene;

如果您想正确理解 Java 中的线程安全,那么我强烈推荐“培训手册”:http ://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601

于 2015-09-07T00:33:13.683 回答