0

У меня есть процесс, который я хочу запускать из разных источников.Как правильно управлять выполнением одного потока?

Предположим, у нас есть один случай, когда мы применяем какой-то другой процесс (назовем его «manualStarter») при определенных условиях хочет вызвать этот основной процесс. Для завершения основного процесса требуется некоторое время, скажем, от 10 секунд до 10 минут. Если процесс уже выполняется, а manualStarter пытается его запустить, он не должен ставиться в очередь более одного раза. Второй процесс, инициирующий запуск основного процесса, может быть «timedStarter», который будет запускать процесс раз в то время, но только если процесс не запущен, иначе он не будет запускать процесс в очередь, вместо этого попытается это снова через некоторое время.

Теперь я попытался реализовать этот менеджер процессов с помощью isAlive() и join(), но кажется, что isAlive() не является надежным вообще, пока он не изменит свое состояние на живое, 100 потоков этого поток может начаться (и иногда). Похоже, я не мог положиться на это.

Затем я попытался использовать службу SingleThreadExecutor, которая ближе к тому, что я ищу, она не блокирует что-либо, и она позволяет только одному потоку выполнять этот процесс, так что это хорошо, однако я до сих пор не знаю, как проверить статус/заблокировать его правильно или как еще я могу гарантировать, что очередь для запуска потока не станет больше 1. Я немного читаю, что семафоры часто используются для подобных задач, но я не уверен как я мог бы использовать их в этом сценарии.

Итак, как я мог достичь того, чего хочу? Нужно ли мне реализовать собственный ThreadPoolExecutor? Как мне это сделать? Есть ли лучший способ?

+1

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

+0

@WasiAhmad да, я верю, что у меня возникают проблемы, чтобы понять - где и как их использовать? –

+0

Просто интересно: не было бы проще не запускать поток, но, возможно, добавить какой-то командный объект в какую-то ** очередь **? Другими словами: просто ваши внешние источники испускают команды в одну очередь; а компонент регулярно сканирует содержимое этой очереди и вызывает этот поток (при необходимости).Просто нажимайте новые команды, и очередь выполняет задание «ах, мы в настоящее время работаем, чтобы можно было отбросить» ?! – GhostCat

ответ

1

Просто используйте общий флаг, чтобы ручной стартер знал, работает ли поток. Например:

// Schedule this to run periodically via ScheduledExecutorService 
class ManualStarter { 
    private final AtomicBoolen isRunning = new AtomicBoolean(false); 
    private ExecutorService exec = Executors.newSingleThreadedExecutor(); 

    public void run() { 
     if (!isRunning.getAndSet(true)) { 
      // It wasn't running so this will start it 
      exec.submit(new MainProcess(isRunning)); 
     } 
    } 
} 


class MainProcess extends Runnable { 
    private final AtomicBoolean isRunning; 

    MainProcess(AtomicBoolean isRunning) { this.isRunning = isRunning; } 

    @Override 
    public void run() { 
     // do whatever it does 
     isRunning.set(false); 
    } 
} 

Тогда где-то запланировать главное, чтобы запустить периодически делать что-то вроде:

ScheduledExectorService sched = Executors.newScheduledThreadPool(1); 
ManualStarter starter = new ManualStarter(); 
// Every 10 seconds will check if MainProcess is running and will start 
// it if it's not 
sched..scheduleAtFixedRate(starter, 0, 10, SECONDS); 
+0

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

+0

Хорошее использование атомных типов. Это было замаскировано в других ответах. – byxor

+0

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

1

Для этого вам следует использовать ExecutorService. Существует несколько доступных реализаций (в том числе ScheduledExecutorService, что позволяет планировать дефисные и/или повторяющиеся задачи - отметьте Executors). Просто выберите тот, который вам подходит лучше всего.

Что касается условного выполнения, то задача проста. Определите какой-либо доступный флаг, который содержит текущее «состояние» заданной задачи. Если он запущен - ничего не делать, если он не запущен - выполнить расписание.

Простой пример:

//our flag 
private volatile AtomicBoolean isRunning=new AtomicBoolean(false); 

public void scheduleTask(){ 
    if(isRunning.get()){ 
     return; // do nothing 
}else{ 
    synchronized(isRunning){ 
    if(isRunning.get()){ 
     return; 
}else{ 
    isRunning.set(true) 
    scheduleNewTask(); 
} 
} 
} 
} 

Для всех как-тос проверить official Oracle's documentaion about Executors. У меня есть использование AtomicBoolean в этом примере издеваться «изменяемый» логическое значение. Это можно сделать также с помощью boolean, но синхронизация должна выполняться на другом объекте (например, private Object lock=new Object();)

+0

Это не является свободным от состояния гонки, описанного – Ivan

+0

SingleThreadExecutor является ExecutorService. Доступный флаг - именно то, что я в настоящее время борется, мне нужно установить его эффективно, чтобы он был заблокирован, как только триггер процесса был запрошен и разблокирован, как только будет завершено выполнение процесса. –

+0

@Ivan - простая синхронизация в методе планирования, и мы потокобезопасны. – Antoniossss