如何正确管理单线程执行?

How to manage single thread execution properly?

我有一个进程,我想由不同的来源触发。

假设我们有一个案例,在某些情况下我们有其他进程(我们称之为 "manualStarter")想要触发这个主进程。主要过程需要一段时间才能完成,比方说 10 秒到 10 分钟。如果在 manualStarter 试图启动它时该过程已经在进行中,则它不应排队多次。触发主进程启动的第二个进程可以是 "timedStarter",它会偶尔触发进程一次,但前提是进程不是 运行,否则它不会将进程排队到被触发,而是稍后再试一次。

现在我已经尝试通过使用 isAlive() 和 join() 来实现这种进程管理器,但似乎 isAlive() 根本不可靠,直到它把它的状态更改为 alive,100 个线程该线程可能会启动(有时会启动)。所以我似乎不能依赖它。

然后我尝试使用 SingleThreadExecutor 服务,它更接近我正在寻找的东西,它不会阻塞任何东西,它只允许单个线程执行进程,所以这很好,但我仍然不知道如何正确检查 status/lock 它,或者我还能如何确保启动线程的队列不会大于 1。我读到一点,信号量通常用于类似类型的任务,但我我不确定在这种情况下如何使用它们。

那么我怎样才能达到我想要的呢?我需要实现自己的 ThreadPoolExecutor 吗?我该怎么做?有没有更好的方法?

您应该使用 ExecutorService for that. There is couple of implementations available (including ScheduledExecutorService that allows you to schedule deffered and/or repeating tasks - check Executors)。只需选择最适合您的需求即可。

至于条件执行,任务很简单。定义某种可访问标志,用于保存给定任务的当前 "state"。如果是 运行 - 什么也不做,如果不是 运行 - 安排执行。

简单示例:

//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 来模拟 "mutable" 布尔值。这也可以用 boolean 完成,但需要在不同的对象上完成同步(例如专用 private Object lock=new Object();

只需使用共享标志,以便手动启动器知道线程是否 运行ning。例如:

// 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);