2011-02-02 35 views
5

假设我有一个ExecutorService(它可以是一个线程池,因此涉及并发),它可以在不同的时间执行一个任务,可以是周期性的,也可以是对其他条件的响应。要执行的任务如下:java:executors +任务+锁

  • 如果此任务已在进行中,则不执行任何操作(并让先前运行的任务完成)。
  • 如果此任务尚未进行,请运行算法X,这可能需要很长时间。

我试图想出一种方法来实现这一点。它应该是这样的:

Runnable task = new Runnable() { 
    final SomeObj inProgress = new SomeObj(); 
    @Override public void run() { 
     if (inProgress.acquire()) 
     { 
      try 
      { 
      algorithmX(); 
      } 
      finally 
      { 
      inProgress.release(); 
      } 
     } 
    } 
} 

// re-use this task object whenever scheduling the task with the executor 

其中SomeObj要么是ReentrantLock(获得= tryLock()和释放= unlock())或的AtomicBoolean或东西,但我不知道它。我需要一个ReentrantLock吗? (也许我想要一个非重入锁,以防algorithmX()导致此任务以递归方式运行!)或者AtomicBoolean是否足够?


编辑:对于非重入锁,这是否合适?

Runnable task = new Runnable() { 
    boolean inProgress = false; 
    final private Object lock = new Object(); 
    /** try to acquire lock: set inProgress to true, 
    * return whether it was previously false 
    */ 
    private boolean acquire() { 
     synchronized(this.lock) 
     { 
     boolean result = !this.inProgress; 
     this.inProgress = true; 
     return result; 
     } 
    } 
    /** release lock */ 
    private void release() { 
     synchronized(this.lock) 
     { 
     this.inProgress = false; 
     } 
    } 
    @Override public void run() { 
     if (acquire()) 
     { 
      // nobody else is running! let's do algorithmX() 
      try 
      { 
      algorithmX(); 
      } 
      finally 
      { 
      release(); 
      } 
     } 
     /* otherwise, we are already in the process of 
     * running algorithmX(), in this thread or in another, 
     * so don't do anything, just return control to the caller. 
     */ 
    } 
} 
+0

建议:不要提交一些复杂的代码并要求某人验证它,请自己尝试,如果遇到不理解的行为,请提出具体问题。 – 2011-02-02 17:14:04

+0

谢谢...这不是我要验证代码,而是我有一种情况,我需要在多线程系统中连续运行一个任务,而且我不知道如何在面对并发问题。我提出的代码仅仅是一个例子。如果我应该使用现有的类,我想知道这一点,因为我正在摸索要问什么精确的问题。 – 2011-02-02 17:17:55

+1

我对并发问题的经验是,尽管你可以尝试使用代码来查看是否存在明显的错误,但不能试着去检查它是否成功,因为可能有奇怪的不可能的拐角条件会显示你的程序不正确,并且可能无法按需重现这些情况。 – 2011-02-02 17:22:31

回答

2

您建议的锁定实施方式很薄弱,因为某些人使用它的方式会很不方便。

下面是一个使用相同的不当使用弱点,你的实现更加高效的实现:

AtomicBoolean inProgress = new AtomicBoolean(false) 
    /* Returns true if we acquired the lock */ 
    private boolean acquire() { 
     return inProgress.compareAndSet(false, true); 
    } 
    /** Always release lock without determining if we in fact hold it */ 
    private void release() { 
     inProgress.set(false); 
    } 
0

ReentrantLock对我来说很好。唯一的情况是,如果您有一个非常短的algorithmX,那么我会发现手动创建锁使用AtomicInteger有趣。

2

你的代码第一位看起来很不错,但如果你对很担心algorithmX递归调用任务,我建议你使用一个java.util.concurrent.Semaphore作为同步对象,而不是ReentrantLock。例如:

Runnable task = new Runnable() { 
    final Semaphore lock = new Semaphore(1); 
    @Override public void run() { 
     if (lock.tryAcquire()) 
     { 
      try 
      { 
      algorithmX(); 
      } 
      finally 
      { 
      lock.release(); 
      } 
     } 
    } 
} 

请特别注意,使用尝试获取。如果获取锁定失败,则不运行algorithmX

0

我认为选择正确的锁impl的秘诀是这样的: *如果这个任务已经在进行中,什么也不做(让先前运行的任务完成)。

在这种情况下,“什么都不做”意味着什么?线程应该阻塞并在运行后重试执行算法X完成?如果是这样的话semaphore.acquire而不是tryAcquire应该被使用并且AtomicBoolean解决方案不能按预期工作。