如何正确管理单线程执行?
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);
我有一个进程,我想由不同的来源触发。
假设我们有一个案例,在某些情况下我们有其他进程(我们称之为 "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);