【问题标题】:Java - running jobs async using ReentrantLock?Java - 使用 ReentrantLock 异步运行作业?
【发布时间】:2026-02-04 06:35:01
【问题描述】:

下面的代码允许我们运行job,同时确保使用ReentrantLock一次只能运行一个作业。

有没有办法修改这段代码以异步运行job.call(),并在启动线程之前将MyConcurrentJobException返回给客户端?

我们尝试将 try/catch/finally 块包装在一个新的 Thread 中,但 unlocklock 必须在同一个线程中发生,所以我们得到一个 IllegalMonitorException

??

final static Lock lock = new ReentrantLock();

public Object runJob(String desc, Callable job, boolean wait) {
    logger.info("Acquiring lock");
    if (!lock.tryLock()) {
        throw new MyConcurrentJobException();
    }

    activeJob = new JobStatus(desc);
    logger.info("Lock acquired");

    try {
        return job.call();
    } catch (MarginServiceAssertionException e) {
        throw e;
    } catch (MarginServiceSystemException e) {
        throw e;
    } catch (Exception e) {
        throw new MarginServiceSystemException(e);
    } finally {
        activeJob = null;
        logger.info("Releasing lock");
        lock.unlock();
        logger.info("Lock released");
    }
}

【问题讨论】:

    标签: java asynchronous reentrantlock


    【解决方案1】:

    您可以使用Semaphore 代替ReentrantLock,它的许可不绑定到线程。

    类似这样的事情(不确定在异步情况下你想对job.call() 的结果做什么):

    final static Semaphore lock = new Semaphore(1);
    
    public void runJob(String desc, Callable job, boolean wait) {
        logger.info("Acquiring lock");
        if (!lock.tryAcquire()) {
            throw new MyConcurrentJobException();
        }
    
        startThread(new Runnable() {
            public void run() {
                try {
                    job.call();
                } finally {
                    lock.release();
                }
            }
        });    
    }
    

    【讨论】:

      【解决方案2】:

      我认为我完全误解了,因为在异步执行某些操作时阻塞和等待对我来说没有太大意义,除非可以在调用线程上取得一些进展。

      你可以这样做吗:

      final static Lock lock = new ReentrantLock();
      final static ExecutorService service = Executors.newThreadPoolExecutor();
      public Object runJob(String desc, Callable job, boolean wait) {
          logger.info("Acquiring lock");
          if (!lock.tryLock()) {
              throw new MyConcurrentJobException();
          }
      
          activeJob = new JobStatus(desc);
          logger.info("Lock acquired");
      
          try {
              Future<?> future = service.submit(job);
              // This next line will block until the job is finished
              // and also will hold onto the lock.
              boolean finished = false;
              Object o = null;
              while(!finished) {
                  try {
                      o = future.get(300, TimeUnit.MILLISECONDS);
                      finished = true;
                  catch(TimeOutException e) {
                      // Do some periodic task while waiting
                      // foot.tapLots();
                  }
               }
               if (o instanceof MarginServiceAssertionException) {
                   throw ((MargineServiceAssertionException)o);
               } else if (o instanceof MargineServiceSystemException) {
                   throw ((MarginServiceSystemException)o);
               } else if (o instanceof Exception) {
                   throw new MarginServiceSystemException(e);
               }
          } catch (... InterruptedException e) { /// catch whatever exceptions throws as part of this
             /// Whatever needs to be done.
          } finally {
              activeJob = null;
              logger.info("Releasing lock");
              lock.unlock();
              logger.info("Lock released");
          }
      }
      

      【讨论】:

      • 我们想使用一个 gui 开始一个工作。我们想通过“你的工作已经开始”消息立即将控制权返回给 gui。但我们想防止并发工作发生.因此,如果用户在另一项工作正在进行时尝试开始一项工作,他们会收到错误。
      • 啊好的,所以我会选择axtavt的解决方案。