Как бороться с потоком SwingWorker, который идет медленно?

#java #multithreading #swing #swingworker

#java #многопоточность #swing #swingworker

Вопрос:

Вряд ли я разбираюсь в многопоточности. И, начиная разбираться в этом, я столкнулся с одной проблемой, которая пришла мне в голову. Недавно я написал одно простое приложение, и как только я получу некоторые новые знания о Java, я хочу улучшить свое приложение с помощью того, что я узнал. Это выглядит как простой графический интерфейс swing, который обновляет изображения каждый период времени. Я внедрил ActionListener и переопределил метод actionPerformed. Таймер с задержкой 15 мс, перерисовал класс JPanel, и все работало нормально. Но я подумал, что обновление моего графического интерфейса с использованием таймера непосредственно в actionPerformed (я предполагаю, что это другой поток, но я едва уверен) — плохая идея. Итак, я решил изменить код и использовать SwingWorker. Я вызвал все свои методы для анимации внутри process () .. и снова приложение работает нормально, но оно стало чрезвычайно медленным. Теперь я думаю, что не так? Почему он работает медленнее, чем раньше? Моя задержка таймера на самом деле не ожидает 15 мс, она намного медленнее, хотя задержка одинаковая. Я допустил ошибку с многопоточностью? Помогите мне разобраться в этом материале. Заранее спасибо

 public class GameEngine() extends SwingWorker<Void, Drawable>
GamePanel gp; // ref to JPanel class
{
 public GameEngine(GamePanel gp)
 {
  this.gp = gp;
 }
}
protected void doInBackground()
{
 publish();
}
protected void process(List<Drawable> chunks)
{
 Timer timer = new Timer(15, e ->
 {
  //methods for animation 
  fall();
  animate();
  checkTouch();
 });
}
  

Некоторый код, который я оставил за рамками. Если вам это нужно, я могу написать…

ИЗДАНИЕ

Просто для ясности моей проблемы я привожу еще несколько примеров и дополнительное объяснение.

** Раньше был: **

 public class GamePanel extends JPanel
{
 public void GamePanel()
 {
 GameEngine engine = new GameEngine(this);
 }
 //some images , variables etc...
  protected void paintComponent(Graphics g)
  super.paintComponent(g)
  g.drawImage(image1, x, y, null);
  g.drawImage(image2, w, z,null);
 ...
}
public class GameEngine () implements ActionListener
{
 GamePanel gp;
 Timer timer;
 public void GameEngine(GamePanel gp)
 {
  this.gp = gp;
  timer = new Timer( 15 , this );
 }
 public void actionPerformed()
 {
  //these methods repaint my GamePanel every 15ms.
  fall(); // make object (image) increment on Y Axis 
  animate(); // make another object (image) decrement on X Axis
  checkTouch(); // check if objects collided
 }
}
  

** Стал: **

 public class GamePanel extends JPanel
{
 public void GamePanel()
 {
 GameEngine engine = new GameEngine(this);
 }
 //some images , variables etc...
  protected void paintComponent(Graphics g)
  super.paintComponent(g)
  g.drawImage(image1, x, y, null);
  g.drawImage(image2, w, z,null);
 ...
}
public class GameEngine () extends SwingWorker<Void, Drawable>
{
 GamePanel gp;
 Timer timer;
 public void GameEngine(GamePanel gp)
 {
  this.gp = gp;
 }
 protected void doInBackground()
 {
  process();
 }
 protected void progress()
 {
  timer = new Timer (15, e-> 
  {
   new ActionListener(new actionPerformed)
   {
      //these methods repaint my GamePanel every 15ms.
  fall(); // make object (image) increment on Y Axis 
  animate(); // make another object (image) decrement on X Axis
  checkTouch(); // check if objects collided
   }
  });
 }
 protected void done()
 {
 };
}
  

Когда я создавал его впервые, я внедрил ActionListener и обновил свою панель с помощью таймера, объявленного в конструкторе.. Я предположил, что это небезопасно для потоков.
Вот почему я передаю все в метод выполнения, где я объявил таймер, который ActionListener в качестве аргумента lambda.
Другими словами, я вызываю все методы для анимации в другом потоке.
Наконец, это стало медленнее, по сравнению с первым примером..
Я не понимаю

  1. Таймер из первого примера EDT или это другой поток?
  2. Мой первый пример потокобезопасен?
  3. Почему мой второй пример идет намного медленнее, чем первый?

Я слышал о ТОМ, что вы НЕ обновляете свой графический интерфейс вне EDT, это тот случай?

Комментарии:

1. Простое создание таймера не приводит к выполнению задачи этого таймера, поэтому это занимает очень мало времени. Перемещение этого создания в фоновый поток не принесет никакой пользы.

2. @VGR был ли мой первый пример потокобезопасным? Разве таймер не означает, что я реализую всю логику в EDT?

3. ДА. ответ arcy, вероятно, правильный; вам нужно перерисовать только прямоугольные области, которые действительно изменились. И, конечно, в вашем коде рисования не должно быть файловых операций ввода-вывода, сетевых операций ввода-вывода или вызовов режима ожидания.

Ответ №1:

В вашем вопросе недостаточно информации, чтобы ответить на него, но я приму ваши запросы о многопоточности и подразумеваемый вопрос о Swing и рендеринге и посмотрю, смогу ли я вам помочь.

Я думаю, что наиболее вероятной причиной вашего замедления является ненужное обновление экрана. Как только пиксель был нарисован на холсте или что-то еще в вашем приложении, обычно вашему приложению не нужно перерисовывать его, если только он не должен измениться; либо спрайт перемещается, либо какое-либо другое изображение в вашем приложении временно закрывает часть рисунка, а затем его необходимо восстановить.

Новички в перерисовке обычно игнорируют это и просто перерисовывают всю окрашенную поверхность. Хотя это и сработает, это медленно; если вы выполняете это несколько раз в цикле, то цикл будет казаться медленным.

Лучший способ сделать это — использовать прямоугольник, переданный в процедуру перерисовки, и перерисовывать только его пересечение со всей поверхностью, перерисованной вашей процедурой — это сокращает часть, которую нужно перерисовать, и, следовательно, время, необходимое для ее перерисовки.

Что касается многопоточности, я думаю, полезно думать об этом так, как мы привыкли думать о вещах в мире с одним процессором — компьютер что-то делает некоторое время, затем останавливается в месте, которое вы не можете предсказать, и некоторое время что-то делает в другом потоке и т.д. Вы не можете предположить порядок, в котором что-то будет сделано, или сколько времени это затратит на каждую вещь и т.д.

На современных многоядерных компьютерах возможно, что эти вещи фактически выполняются одновременно, но я не уверен, что попытка представить это вам как-то поможет.

Комментарии:

1. Я работал хорошо, это замедлилось, как только я использовал SwingWorker… Пожалуйста, смотрите выпуски

2. Извините, я не знаю, что еще сказать после ваших правок. Они не будут компилироваться, как указано, кстати, я подозреваю, что фигурные скобки вокруг операторов в paintComponent() были каким-то образом потеряны. Я бы повторил, что я не знаю, что это ваша проблема, только то, что она распространенная. Где-то еще вы спрашиваете об обновлении пользовательского интерфейса: да, сделайте это вне потока отправки событий.