#java #multithreading #thread-sleep #coding-efficiency
#java #многопоточность #поток-сон #кодирование-эффективность
Вопрос:
Здесь у меня есть немного кода, который работает так, как я хочу. Все, что это — обратный отсчет в секундах до определенной даты, которую я определяю в коде. Я использую Thread.currentThread().sleep(1000);
для обновления JLabel с текущим временем, оставшимся до даты. Проблема в том, что JLabel не обновляется каждую секунду, как предполагалось. Иногда он обновляется каждые 2 секунды, в других случаях для обновления требуется целых 10 секунд. Я считаю, что это как-то связано с тем, как я вызываю свои методы, но я не совсем уверен, как сделать это более эффективным.
Вот основной метод, который вызывает метод для обновления JLabel в потоке:
public static void main(String args[])
{
initUI();
try
{
while(true)
{
Thread.currentThread().sleep(1000);
getTime();
}
} catch(Exception e){System.out.println("An error has occured...");}
}
Вот метод, вызываемый методом, вызываемым методом main . Этот метод в конечном итоге отправляет переменную, оставшуюся в секундах, в 3-й метод:
public static void getTime()
{
Calendar c = Calendar.getInstance();
// Gets abstract current time in ms
long now = c.getTimeInMillis();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
// Gets current time in ms
long msPassed = now - c.getTimeInMillis();
// There are 86,400,000 milliseconds in a day
// Gets the seconds remaining in the day
long secRemaining = (86400000 - msPassed) / 1000;
//-----------------------------------------------------//
// Creates a new calendar for today
Calendar cal = Calendar.getInstance();
int currentDayOfYear = cal.get(Calendar.DAY_OF_YEAR);
// Creates a calendar for November 20th, 2016
Calendar aniv = new GregorianCalendar(2016,10,20);
aniv.set(Calendar.MONTH, 10);
aniv.set(Calendar.DAY_OF_MONTH, 20);
int aniversary = aniv.get(Calendar.DAY_OF_YEAR);
remaining = ((aniversary - currentDayOfYear) * 24 * 60 * 60) secRemaining;
setTextOnScreen(remaining);
}
И, наконец, это метод, который перезаписывает JLabel (вызывается описанным выше методом):
public static void setTextOnScreen(long num)
{
text.setForeground(Color.GREEN);
text.setLocation((int)width/2 - 150, 50);
text.setFont(new Font("Monospaced", Font.BOLD, 48));
text.setSize(300,150);
text.setText("" num);
panel.add(text);
}
Я не включаю остальную часть кода, потому что это должно быть неактуальным, но если вы тоже хотите это увидеть, просто дайте мне знать.
Комментарии:
1. Ваш сон длится 2 секунды
Ответ №1:
Две проблемы:
-
Вы вызываете
text.setSomething()
иpanel.add
из фонового потока, который не является потоком пользовательского интерфейса, который вы должны использовать. Попробуйте использоватьSwingUtils.invokeLater()
илиSwingUtils.invokeAndWait()
и вызвать код, который касается пользовательского интерфейса. -
После вызова ваших
set
методов вы также должны вызватьtext.invalidate()
сигнал о том, что компоненты пользовательского интерфейса необходимо обновить … в противном случае поток пользовательского интерфейса не заметит, что компонент необходимо переизмерить и перерисовать.
Ответ №2:
Вы должны обновлять компоненты GUI только в цикле событий. Если вы попытаетесь обновить их в других потоках, вы можете получить непредсказуемые результаты. Я предлагаю использовать таймер переключения для периодических задач, которые будут выполняться в цикле событий GUI.
Ответ №3:
Вы могли бы попробовать использовать Timer
У него есть метод #scheduleAtFixedRate
, который примет задачу для выполнения, время для начала ее выполнения и интервал, с которого он должен выполнить задачу.
Оберните запланированные действия в класс, который расширяется TimerTask
для использования с этим методом.
Вот javadocs для этого класса в Java 7 https://docs.oracle.com/javase/7/docs/api/java/util/Timer.html