开发者

Problem related to variable persistence between threads in java

all. A thread in a Java program I am working on animates a random walk in space (or at least, it will once this problem is resolved). It contains the following two methods:

public void run() {
    while(gw.checkBoundingBox()) {
        if(!gw.pause) step();
    }       
}

public void step() {

    Point3d p1, p2;

    //get the last point, step, get the new point
    p1 = new Point3d(gw.position);
    gw.randomStep();
    p2 = new Point3d(gw.position);

    //create the Alpha that will do the animation, and wrap it in an AlphaControl,
    //which will set this object's pause flag until the Alpha finishes
    Alpha alpha = new Alpha();
    alpha.setLoopCount(1);

    if(alpha.finished()) System.out.println("DEBUG: I'm already dead.");

    AlphaControl ac = new AlphaControl(alpha,gw);
    ac.start();

    //create a piece of the path, attach an interpolator to do the animation
    PathCyl cyl = new PathCyl(p1,p2);

    StretchInterpolator si = new StretchInterpolator(alpha, cyl.anchor);
    si.setSchedulingBounds(new BoundingSphere(new Point3d(0,0,0),50));
    cyl.anchor.addChild(si);

    //put it all together
    BranchGroup b = new BranchGroup();
    b.addChild(cyl.tg);
    trans.addChild(b);  
}

So if the pause flag is not set, it runs step(). The only part of step() that you need to look at is the few lines about the Alpha ob开发者_如何学编程ject. (Alpha is an API class that produces a time-dependent function used for animating). So step() creates an Alpha object, which it then feeds to another thread called AlphaControl. AlphaControl tells the program to stop calculating points until this step is done animating. It does this by setting the pause flag that is checked in the run() method.

So what's the problem? Note that I added a debug line that immediately checks if the Alpha is finished after it is created. It seems like this line of code should never execute. Is the Alpha finished? Of course not, we just created it. But this line executes every time that the function is called AFTER the first time. Somehow it is hanging onto the same Alpha instance and using it over and over. I assume that this is because of the reference to the Alpha that is still alive in the AlphaControl thread.

So how do I fix this? I have tried several things. I created a huge array of Alphas, initialized them all before the walk even started, and tried to tell it to use a different alpha from the array at every step, but this had the same result. I also tried using the AlphaControl to set alpha to null before it closes, but this didn't work either. Is it possible to destroy this object? By the time the step() code gets back to running again, the AlphaControl that was created the first time around should be done and waiting for garbage collection.

Also, just in case it would be helpful to see it, here is the AlphaControl class.

public class AlphaControl extends Thread {

public Alpha alpha;
public GraphicalWalker gw;

public AlphaControl(GraphicalWalker gw, Alpha alpha) {
    this.gw = gw;
    this.alpha = alpha;
}

public void run() {

    boolean stop = false;
    boolean finished;

    while(!stop) {
        finished = alpha.finished();

        if( !finished && !gw.pause ) gw.pause = true;
        if( finished && gw.pause )   gw.pause = false;
        if(finished) stop = true;
    }
}

}

Thanks in advance for any help. Jeff


I think key to understanding why your code fails is understanding what the Alpha class of the Java3D library does. From your question it seems to me you do not understand it correctly. It does not loop. If you check the source code (http://www.java2s.com/Open-Source/Java/6.0-JDK-Modules/java-3d/javax/media/j3d/Alpha.java.htm) you will see it does not contain a single loop structure. What it does do, is define a function that maps a time value to a value in the range [0,1].

If you look at the source of the finished() method :

    /**
     * Query to test if this alpha object is past its activity window,
     * that is, if it has finished looping.
     * @return true if no longer looping, false otherwise
     */
    public boolean finished() {
        long currentTime = paused ? pauseTime : J3dClock.currentTimeMillis();
        return ((loopCount != -1) &&
           ((float)(currentTime - startTime) * .001f > stopTime));
    }

you notice that its value depends on when it is called.

And since you basically defined your alpha to have 1 loop of 1 millisecond, starting from the time of creation it will not be finished for 1 millisecond after you create that alpha, and finished ever after.

I hope this helps.


I'm not sure if I completely follow the intent of the program, but it seems like you have a race condition. When you call ac.start() on your AlphaControl object, there is no guarantee that gw.pause will be set prior to the next round of your loop executing. In fact, there's a fair chance that the loop will execute many times prior to gw.pause getting set.

Just for kicks, I suggest calling ac.run() instead of ac.start() on a run-through, as a shortcut to take threading out of the picture. If the intended behavior occurs, then I would look hard at your reason for doing this in multiple threads instead of a single one.


I think the problem is one of two issues:

  • Alpha's state is kept as a static variable, meaning that it's the same for all instances of Alpha, and you're likely not resetting it in the constructor (rightfully so).
  • Some part of your state isn't marked volatile, which breaks the semantics the compiler relies on to know that a marked field will always require a fresh value lookup (otherwise the compiler is free to cache the value)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜