#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.
Другими словами, я вызываю все методы для анимации в другом потоке.
Наконец, это стало медленнее, по сравнению с первым примером..
Я не понимаю
- Таймер из первого примера EDT или это другой поток?
- Мой первый пример потокобезопасен?
- Почему мой второй пример идет намного медленнее, чем первый?
Я слышал о ТОМ, что вы НЕ обновляете свой графический интерфейс вне EDT, это тот случай?
Комментарии:
1. Простое создание таймера не приводит к выполнению задачи этого таймера, поэтому это занимает очень мало времени. Перемещение этого создания в фоновый поток не принесет никакой пользы.
2. @VGR был ли мой первый пример потокобезопасным? Разве таймер не означает, что я реализую всю логику в EDT?
3. ДА. ответ arcy, вероятно, правильный; вам нужно перерисовать только прямоугольные области, которые действительно изменились. И, конечно, в вашем коде рисования не должно быть файловых операций ввода-вывода, сетевых операций ввода-вывода или вызовов режима ожидания.
Ответ №1:
В вашем вопросе недостаточно информации, чтобы ответить на него, но я приму ваши запросы о многопоточности и подразумеваемый вопрос о Swing и рендеринге и посмотрю, смогу ли я вам помочь.
Я думаю, что наиболее вероятной причиной вашего замедления является ненужное обновление экрана. Как только пиксель был нарисован на холсте или что-то еще в вашем приложении, обычно вашему приложению не нужно перерисовывать его, если только он не должен измениться; либо спрайт перемещается, либо какое-либо другое изображение в вашем приложении временно закрывает часть рисунка, а затем его необходимо восстановить.
Новички в перерисовке обычно игнорируют это и просто перерисовывают всю окрашенную поверхность. Хотя это и сработает, это медленно; если вы выполняете это несколько раз в цикле, то цикл будет казаться медленным.
Лучший способ сделать это — использовать прямоугольник, переданный в процедуру перерисовки, и перерисовывать только его пересечение со всей поверхностью, перерисованной вашей процедурой — это сокращает часть, которую нужно перерисовать, и, следовательно, время, необходимое для ее перерисовки.
Что касается многопоточности, я думаю, полезно думать об этом так, как мы привыкли думать о вещах в мире с одним процессором — компьютер что-то делает некоторое время, затем останавливается в месте, которое вы не можете предсказать, и некоторое время что-то делает в другом потоке и т.д. Вы не можете предположить порядок, в котором что-то будет сделано, или сколько времени это затратит на каждую вещь и т.д.
На современных многоядерных компьютерах возможно, что эти вещи фактически выполняются одновременно, но я не уверен, что попытка представить это вам как-то поможет.
Комментарии:
1. Я работал хорошо, это замедлилось, как только я использовал SwingWorker… Пожалуйста, смотрите выпуски
2. Извините, я не знаю, что еще сказать после ваших правок. Они не будут компилироваться, как указано, кстати, я подозреваю, что фигурные скобки вокруг операторов в
paintComponent()
были каким-то образом потеряны. Я бы повторил, что я не знаю, что это ваша проблема, только то, что она распространенная. Где-то еще вы спрашиваете об обновлении пользовательского интерфейса: да, сделайте это вне потока отправки событий.