2013-12-22 5 views
0

У меня есть простой класс 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"); 
} 

Нить продолжает работать бесконечно (работает через для петли до бесконечности), даже если он должен быть в состоянии выполнить задачу в течение нескольких секунд. genComplete никогда не соответствует истине.

Редактировать: нить создается и наблюдается с экрана загрузки графического интерфейса пользователя, который изменяется, когда значение genComplete истинно.

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 
} 
+3

Вам нужно будет показать больше участвующих классов, в том числе о том, как вы создаете и запускаете свою нить. –

ответ

2

Mutable state убивает многопоточные 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(); 
    } 
    ... 
} 
+2

+1. Или сделайте его AtomicBoolean или сделайте его изменчивым. –

+0

volatile, вероятно, работает лучше.Я всегда ненавидел синхронизацию, поскольку мне кажется, что я «публикую» или раскрываю то, что должно быть внутренним для моего объекта. –

1

Я согласен с комментарием о неизменности, он всегда предпочитал использовать неизменные объекты, , но я думаю, что синхронизируется не будет лучшим решением,

Вы могли бы быть лучше, используя «Executors "вместо потоков. вы можете прочитать об этом в следующем коротком уроке в блоге Jenkov в: Jenkov

И вы хотите, чтобы перейти к функции «вызов» реализация, которая реализует интерфейс Callable, который возвращает «Future» объект, который вы можете спросите, завершена ли задача.

Вы можете использовать функцию блокировки «получить», которая будет ждать завершения процессов.

Надеюсь, что я правильно понял и что ответ вам поможет.

Удачи!

1

Это только гипотеза, но, похоже, вы получаете функцию spin-lock с методом update(), которая оптимизируется JVM после нескольких итераций, так что genComplete кэшируется, а spinlocking thread никогда не определяет значение genComplete. Когда ваш метод x() пуст, метод run() заканчивается быстро, а JVM havent еще не оптимизирует ваш код (по умолчанию Oracle HotSpot JIT включает оптимизаторы после 1500 вызовов методов в клиентском режиме), но операции ввода-вывода 1) блокируются (что означает больше времени процессора для других потоков) 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 Concurrency на практике.

Смежные вопросы