#java #multithreading #thread-safety
#java #многопоточность #безопасность потоков
Вопрос:
У меня есть следующий метод, который вызывается каждый раз, когда я нажимаю на кнопку, это приводит к запуску нового потока снова и снова при нажатии кнопки, что приводит к многократной инициализации потока, однако я хочу, чтобы выполнялся только один поток, как я могу этого добиться.
private void scheduleMessages() {
new Thread(new Runnable() {
@Override
public void run() {
//Some operations
}
}).start();
}
Примечание: это небольшой метод, и я не хочу создавать отдельный класс, просто чтобы сделать его одноэлементным, поэтому решение без одноэлементного шаблона будет оценено.
Комментарии:
1. Возможно, заблокируйте эту кнопку, пока поток не завершит свою работу (или не будет прерван)
2. вы могли бы попытаться присвоить экземпляр потока некоторой переменной. Перед тем, как перезапустить новый поток в вашем методе, вы можете проверить, есть ли у переменной уже назначенный ей какой-либо поток или нет
3. Но хотите ли вы выполнять каждый «щелчок» или кнопка «отключена» во время выполнения? Если вам нужно запускать каждый щелчок, используйте фиксированный пул потоков и отправляйте в него каждый щелчок (с одним потоком). Если нет, идея флага Лукаса работает нормально, но я бы попытался переписать это, чтобы быть потокобезопасным, два потока могут быть выполнены, если
scheduleMessages
метод вызывается дважды перед запуском первого потока.
Ответ №1:
если вы не можете создать экземпляр этого для проверки isActive()
, вам следует создать переменную семафора — логическое значение, которое вы устанавливаете true
при запуске потока и устанавливаете false
, когда вы закончите.
private void scheduleMessages() {
if (!taskRunning){
new Thread(new Runnable() {
@Override
public void run() {
taskRunning = true;
//Some operations
taskRunning = false;
}
}).start();
}
}
Комментарии:
1. К вашему сведению: это небезопасно для потоков, вы можете просто синхронизировать метод и установить
taskRunning = true
внеRunnable
.
Ответ №2:
Пусть этот поток будет фоновым потоком — возможно, инициализируйте его при первом нажатии кнопки.
Пусть этот поток прослушивает очередь и обрабатывает сообщения в этой очереди.
Всякий раз, когда кнопка нажимается снова, помещайте новое сообщение в очередь.
Ответ №3:
Если вам нужно выполнять все запросы, но для определенного количества потоков, вы можете использовать пул потоков и позволить исполнителю управлять очередью.
private ExecutorService services;
private final static int POOL_SIZE = 1;
public MessagesService(){
services = Executors.newFixedThreadPool(POOL_SIZE);
}
public void scheduleMessages(Runnable r){
services.submit(r);
}
Если вы вызываете addCall
x раз, x поток будет выполнен в конце, но никогда не будет использовать больше, чем количество потоков, доступных в пуле. Здесь 1 поток.
Для системы, которая принимает только один запрос, вы можете использовать тот же подход, но проверить Future
возвращенный одним исполнителем потока. Таким образом, вы можете проверить состояние службы.
private ExecutorService services;
private Future<?> lastCall;
public MessagesService() {
services = Executors.newSingleThreadExecutor();
lastCall = null;
}
public synchronized void scheduleMessages(Runnable r) {
if(!isScheduled()){
lastCall = services.submit(r);
}
}
public boolean isScheduled(){
return lastCall != null amp;amp; !lastCall.isDone();
}
Таким образом, Runnable
не нужно обновлять флаг, который дает повторно используемое решение.
Вот пример Runnable
для тестирования этих кодов :
new Runnable() {
System.out.println("Running");
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}