主线程一直在等待新线程

时间:2012-04-25 18:34:07

标签: java multithreading

我正在尝试为我的项目编写一些“游戏引擎”,我遇到了线程问题。当我在主流程中创建一个Thread(LoadThread)时,它会一直等到Run();在LoadThread结束。

//loading thread
public class LoadThread implements Runnable{
    private boolean running = false;
    private Thread loader = null;

    public LoadThread(/*init data structures*/){
        loader = new Thread(this);
    }

    public void start(){
        running = true;
        run();
    }

    synchronized public void run() {
        System.out.println(" loading started ");
        while(running){            
            //do some loading, when done, running = false
        }
        System.out.println(" loading done ");
    }
}

//holds data, starts loading
public class SourceGod {
    private LoadThread img_loader;
    public void startLoading(){
        img_loader = new LoadThread(/* some data structures */);
        img_loader.start();
    }
}

//runs the game
public class Game extends GameThread implements ActionListener{
    private SourceGod sources;
    public Game(Window full_screen){
        sources = new SourceGod(/* some data structures */);
        System.out.println("before");
        sources.startLoading();
        System.out.println("after");
    }
}

//own thread to refresh
abstract public class GameThread extends JPanel implements Runnable{
    //anything from there is not called before "after"
}

输出

before
loading started
//some loaded data report, takes about 2-3s
loading done
after

任何帮助将不胜感激。 (更多代码http://paste.pocoo.org/show/orCfn9a8yOeEQHiUrgjG/) 谢谢, Vojtěch

2 个答案:

答案 0 :(得分:5)

您的问题是您在构造函数中创建了一个Thread对象,但是当您调用start()时,您没有启动该线程,而是在同一个线程中运行run()方法。这似乎是围绕扩展 Thread实施 Runnable的混淆。

我认为你应该做的事情是:

// this should be of type Thread not LoadThread
private Thread img_loader;
...
// don't create the loader thread inside of LoadThread
img_loader = new Thread(new LoadThread(/* some data structures */));
img_loader.start();

LoadThread是线程将执行的类型Runnable的类。使用您当前的代码,当它调用时:

img_loader.start();

它实际上并没有启动新的Thread,它只是调用LoadThread.start()方法,该方法在同一个线程中调用run()

public void start(){
    running = true;
    run();
}

修改

在您提供的链接中仔细查看已发布的代码时,您将在Thread构造函数中创建LoadThread对象。这不是一个好的模式:

public LoadThread(/*init data structures*/) {
    // not recommended IMO
    loader = new Thread(this);
}

再次,当您调用LoadThread.start()时,您没有启动该线程,而是在同一个线程中调用run()。我会改用new Thread(new LoadThread())模式。如果您不得不包裹Thread内的LoadThread ,那么您应该将LoadThread.start()更改为:

// this should be removed, you want to call Thread.start() instead
public void start(){
    running = true;
    // this will start the internal thread which will call run()
    loader.start();
}

顺便说一下,如果你想在另一个帖子中将running设置为false,那么它应该被标记为volatile

答案 1 :(得分:0)

使用Runnable接口的方式有几个问题:

  • runnable是一种包装代码的方法,以便可以在不同的线程中启动它。 您应该覆盖run方法。不要直接打电话,永远不要。
  • 使用runnable,包装进一个线程并运行线程启动方法。因此,您不应该覆盖线程的start方法,或者始终调用super.start()。
  • 为runnable提供启动方法只是凌乱。包装线程的start方法将在新线程(包装器)中调用runnable的run方法。而已。因此,如果你的runnable中有一个start方法,它将永远不会被包装线程调用。
  • 从不调用线程或runnable的run方法。您将错过这一点,因为该方法将在当前线程中执行,而不是在新线程中执行。
  • 不同步run方法,它只能由一个线程使用。

以下是您应该做的事情:

//loading thread
public class LoadThread implements Runnable{
    /** Wether or not the thread is running. */
    private boolean running = false;
    /** Wrapper thread. */
    private Thread loader = null;

    public LoadThread(/*init data structures*/){
        loader = new Thread(this);
    }

    public void start(){
        running = true;
        loader.start();
    }

    @Override
    public void run() {
        System.out.println(" loading started ");
        while(running){            
            //do some loading, when done, running = false
        }
        System.out.println(" loading done ");
        running = false;
    }
}

如您所见,如果您想要一个start方法,只需调用包装器线程启动方法。

另外,如果你真的需要调用start,请考虑不直接使用runnable而是使用线程:

 //loading thread2
 public class LoadThread extends Thread{
    /** Wether or not the thread is running. */
    private boolean running = false;

    @Override
    public void run() {
        running = true;
        System.out.println(" loading started ");
        while(running){            
            //do some loading, when done, running = false
        }
        System.out.println(" loading done ");
        running = false;
    }
}

这简单得多。