【问题标题】:How to execute a @Scheduled job in a thread safe manner in SpringBoot?如何在 Spring Boot 中以线程安全的方式执行 @Scheduled 作业?
【发布时间】:2021-01-12 15:18:05
【问题描述】:

我有一个将元素添加到集合的服务类,并且有一个计划的作业方法每 2 秒执行一次,它读取添加到集合中的所有元素(服务添加元素的那个),最后,它清除了整个集合。

如果在我从集合中读取数据之间或在完成从集合中读取数据之后到清除集合之前的时间之间添加了某些元素,我会感到困惑,那么该元素将丢失。

如何确保在计划作业未完成时没有元素被添加到集合中?

EventService.java


public void foo(){
    eventSet.add(new Event("event description"));
}

EventJob.java

@Autowired
EventService eventService;

@Scheduled(cron = "${cron.expression.for.every.2.second}")
private void job(){
    for(Event event : eventService.getEventSet()){
         //process event
         System.out.println(event);
    }
    eventService.getEventSet().clear();
}

【问题讨论】:

    标签: java spring multithreading spring-boot


    【解决方案1】:

    据我了解,您可以为此行为创建线程控件,

    private static ReentrantLock listLock = new ReentrantLock();
    
      private void readSomeData() {
        // Get the lock of object
        listLock.lock();
    
        // read list
    
        // free the lock
        listLock.unlock();
      }
    
      @Scheduled(cron = "${cron.expression.for.every.2.second}")
      private void job() {
        // Check lock is free or not
        if (!listLock.isLocked()) {
          // Get the lock of object
          listLock.lock();
          for (Event event : eventService.getEventSet()) {
            // process event
            System.out.println(event);
          }
          eventService.getEventSet().clear();
        }
        // free the lock
        listLock.unlock();
      }
    

    【讨论】:

      【解决方案2】:

      如果您不想在运行计划作业时被添加到集合中,则意味着您必须根据某些设置限制维护一些标志,并在您达到限制时启用 AtomicBoolean 为真“假”一旦你已经清除了集合。 下面是伪代码,

      AtomicBoolean canAddData = new AtomicBoolean(true);
      
      public void foo(){
          if(canAddData){ // here instead of atomicboolean you can use eventSet.size()==10 as well if you want to be added based on some level size.
            eventSet.add(new Event("event description"));
            canAddData.set(eventSet.size() <= 10)
         }
      }
      
      // scheduler code 
      public void processEvents() {
           Iterator<Event> eventIter = eventSet.iterator()
           while(eventIterator.hasNext()) {
                Event = eventIterator.remove()
                // Process event here and clearing item one by one
           }
           canAddData.set(true)
      }
      

      【讨论】:

      • 在 foo() 内部,我想在计划的作业完成时继续添加事件。怎么做?
      • 我也在那里添加了逻辑。根据调度程序中的大小,您必须重置布尔值并相应地设置布尔值。
      【解决方案3】:

      您应该知道,如果您在多机集群中,则会出现其他问题,而这种“内存中”解决方案无法解决。

      private AtomicBoolean isRunning = new AtomicBoolean(false);
      
      public void job(){
           Set<YourObject> events = eventService.getEventSet();
           if (isRunning.compareAndSet(false, true)) {
              for(Event event : events){
                  //process event
              }
             
              eventService.getEventSet().clear();
              isRunning.compareAndSet(true, false);
           }
           
      }
      

      在操作澄清后编辑!

      【讨论】:

      • Ricardo,我不希望在执行计划作业时同时修改集合,而 CopyOnWriteArraySet 将允许这样做。
      • 知道了。你能编辑你的问题吗?
      • 在 foo() 内部,我想在计划的作业完成时继续添加事件。怎么做?
      • 你的问题不能再清楚一点吗?
      猜你喜欢
      • 2011-09-29
      • 1970-01-01
      • 2021-02-27
      • 1970-01-01
      • 2020-03-24
      • 1970-01-01
      • 2020-01-03
      • 2020-06-06
      • 2016-11-17
      相关资源
      最近更新 更多