有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java避免同时执行两次Spring@Async任务

我只是在Spring框架中学习多线程,我不知道如何处理一个案例。我有一个持久的操作,我不想让用户等待它完成,我发现有一个@Async注释将该方法标记为异步可执行

我的问题是,阻止这种方法的最佳方式是什么,这样来自同一家公司的用户就不能在同一时间执行它。为了准确,我甚至想阻止执行分析数据(…)分析统计学(…)由同一公司的用户在同一时间发布

我在考虑使用ConcurrentHashMap,用户公司作为键,布尔值作为值,并在执行操作之前检查它。我想知道我的方向是否正确,或者Spring是否提供了其他更合适的选择

@Service
public class LongOperationService {

   @Async
   public void analyzeData(User user, List<String> data) {
       boolean operationResult = performLongOperation(data);
       if (opeartionResult) {
           log.info("Long operation ended successfully")
       } else {
           log.error("Long operation failure")
       }
   }

   @Async
   public void analyzeStatistics(User user, List<String> statistics) {
       ...
   }

   private void performLongOperation(List<String> data) {
        // Just for demonstration
        Thread.sleep(10000);
        return true;
   }
}

public class User {
   String username;
   String company;
}

共 (2) 个答案

  1. # 1 楼答案

    可以使用Semaphore来限制访问资源的线程数

    由于您希望防止来自同一公司的用户同时访问您的分析功能,因此您应该为每个公司创建信号量:

    // Init on startup
    // Key should be a unique identifier to a company, I assume the `String company` as key, you should adjust as your real requirement
    static final Map<String, Semaphore> COMPANY_ENTRANT = new ConcurrentHashMap<>();
    // for each company
    COMPANY_ENTRANT.put(companyId, new Semaphore(1));
    

    现在为您服务:

    @Async
    public void analyzeData(User user, List<String> data) {
       Semaphore entrant = COMPANY_ENTRANT.get(user.getCompany());
       try {
           entrant.acquire();
           try {
                  boolean operationResult = performLongOperation(data);
                  if (opeartionResult) {
                      log.info("Long operation ended successfully")
                  } else {
                      log.error("Long operation failure")
                  }
           } finally {
              entrant.release();
           }
    
       } catch(InterruptedException e) {
           ...
       }
    
    }
    

    如果想要延迟初始化COMPANY_ENTRANT映射,可以使用putIfAbsent

     Semaphore entrant = COMPANY_ENTRANT.putIfAbsent(user.getCompany(), new Semaphore(1));
    
  2. # 2 楼答案

    试试这样:

    private final Set<String> runningOperations = Collections.synchronizedSet(new HashSet<>());
    private final Object lock = new Object();
    
    @Async
    public void analyzeData(User user, List<String> data) throws Exception {
        synchronized (lock) {
            if (runningOperations.contains(user.company))
                return;
            runningOperations.add(user.company);
        }
        try {
            boolean operationResult = performLongOperation(data);
            if (operationResult) {
                log.info("Long operation ended successfully");
            } else {
                log.error("Long operation failure");
            }
        } finally {
            runningOperations.remove(user.company);
        }
    }