为什么这个线程永远不会完成自己



我有一个简单的 Runnable 类,它应该遍历世界数据的两个维度,并在每个点执行一个函数。 我还有一个布尔值和一个返回方法,可以告诉线程何时完成。

public class Generator implements Runnable
{
private World world;
private Random seed;
private boolean genComplete = false;
public Generator(World world)
{
    // Implementation
}
public boolean isComplete()
{
    return genComplete;
}
/**
 * Begins world generator thread.
 */
@Override
public void run()
{
    world.initializeWorldTileData();
    generateWholeWorld();
    System.out.println("Completed");
    genComplete = true;
}
/**
 * Processes entire world generation tree.
 */
private void generateWholeWorld()
{
    world.saveData();
    for (int x = 0; x < world.getWorldSizeX(); x++)
    {
        for (int y = 0; y < world.getWorldSizeY(); y++)
        {
            // Example function.
            x();
        }
    }
}
private void x()
{
    // When nothing is performed genComplete equals true.
}
}

在此示例中,当运行时,将完全执行 generateWholeWorld() 方法并打印Completed。 但是,如果我在x()中添加任何函数:

private void x()
{
       System.out.println("Example function");
}

线程保持无限运行(无休止地运行 for 循环),即使它应该能够在几秒钟内执行任务。 genComplete永远不等于真实。

编辑:正在从 GUI 加载屏幕创建和观察线程,当genComplete为 true 时会更改。

private Thread generator;
private boolean doneGenerating;
public ScreenGenerateWorld(Screen parent, InitialWorldSettings settings)
{
    super(parent);
    world = World.createNewWorld(settings);
    this.generator = new Thread(world.getWorldGenerator());
    generator.start();
}
@Override
public void update()
{       
    doneGenerating = world.getWorldGenerator().isComplete();
    if (doneGenerating)
    {
        info.setText("Press "A" to Continue...");
        if (Keyboard.isKeyDown(Keyboard.KEY_A))
            AntFarm.getAntFarm().changeActiveScreen(new ScreenWorld(parent, world));
    }
    // Render code
}
可变状态在

多线程 Java 应用程序中会终止。 我强烈建议您在该genComplete状态变量周围进行一些同步,以便所有线程对其值有一个共同的视图。

public class Generator implements Runnable 
{
    ...
    private boolean genComplete = false;
    public synchronized void setComplete() {
        getComplete = true;
    }
    public synchronized isComplete() {
        return genComplete;
    }
    ...
    /**
     * Begins world generator thread.
    */
    @Override
    public void run()
    {
        world.initializeWorldTileData();
        generateWholeWorld();
        System.out.println("Completed");
        setComplete();
    }
    ...
}

我同意关于不可变性的评论,总是首选使用不可变对象,但我认为同步不是最好的解决方案,

最好使用"执行程序"而不是线程。您可以在Jenkov博客的下一个简短教程中阅读有关它的信息:Jenkov

并且您需要将一个实现接口 Callable 的实现传递给 "call" 函数,该接口返回一个"Future"对象,您可以询问任务是否完成。

您可以使用"get"阻止功能来等待进程完成。

我希望我正确理解了您的需求,并且答案对您有所帮助。

祝你好运!

这只是一个假设,但似乎你得到了一个带有update()方法的自旋锁,该方法在几次迭代后由JVM优化,以便缓存genComplete并且旋转锁定线程永远不会确定genComplete的值更改。当你的方法x()为空时,方法run()完成得很快,JVM还没有优化你的代码(默认情况下,Oracle HotSpot JIT在客户端模式下调用1500次方法后打开优化),但I/O操作是1)阻塞(这意味着其他线程有更多的CPU时间)2)不是很快,所以当x()包含System.out.println()时,优化就会参与。易失性将解决这种情况下的问题。

我最好建议你使用执行器和回调。例如:

button.addActionListener(new ActionListener() { 
    public void actionPerformed(ActionEvent e) { 
        button.setEnabled(false); 
        label.setText("busy"); 
        backgroundExec.execute(new Runnable() { 
            public void run() { 
                try { 
                    doBigComputation(); 
                } 
                finally { 
                    GuiExecutor.instance().execute(new Runnable() { 
                        public void run() { 
                            button.setEnabled(true); 
                            label.setText("idle"); 
                        } 
                    }); 
                } 
            }
        }); 
    } 
});

更多关于这一点的信息,你可以从 Java 并发实践中学习。

最新更新