当计数器达到X时,线程池并停止执行for循环

时间:2015-01-23 17:51:47

标签: java multithreading spring

我对线程池有点困惑,并从for循环中提供退出条件。关于如何正确地做到这一点,我还没有找到合适的解释。我一直在尝试一些可能性,但我被困住了

我有这段代码。

  @Override
  @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Throwable.class)
  public void auditAllDomainConfigurationStatuses() {
    logger.info("Starting audit of all domain configuration statusses");
    int errorStatusCounter = 0;
    Map<SubdomainRegistryStatus, List<String>> domainsByStatus = new HashMap<SubdomainRegistryStatus, List<String>>();
    List<DomainConfigurationStatus> domains = domainConfigurationStatusDao.findAll();
    for (DomainConfigurationStatus domainConfigurationStatus : domains) {
      String domainName = domainConfigurationStatus.getDomainName();      
      DomainConfigurationStatus result = domainConfigurationStatusAuditor.auditDomainConfigurationStatus(domainConfigurationStatus.getId());
      addDomainToDomainsByStatusMap(domainsByStatus, result, domainName);
      if(SubdomainRegistryStatus.ERROR.equals(result.getStatus())){
        errorStatusCounter++;
        if(errorStatusCounter >= EMERGENCY_AUDIT_STOP_LIMIT){
          logger.error("Emergency audit stop more then " + EMERGENCY_AUDIT_STOP_LIMIT + " records went into status ERROR");
          mailEmergencyDomainConfigurationStatusAuditStop();
          return;
        }
      }else{
        errorStatusCounter = 0;
      }      
    }

    mailDomainConfigurationStatusReport(domainsByStatus);
    logger.info("Audit of all domain configuration statusses completed");    
  }

此代码将在某处调用域的dns来获取它的ip。然后它将更新数据库中的状态。相当简单的事情。但是,如果X之后的状态转换为ERROR,那么企业希望我们停止整个过程。我设法写了这个,用上面的方法很简单。然而,调用dns来获取ip的速度很慢,我每秒可以处理大约6个域。我们必须处理超过32 000个域名。我们需要提高性能,因为这种多线程是可取的。

所以我开始编程任务,在spring等创建一个线程池...然后我意识到等待EMERGENCY_AUDIT_STOP_LIMIT如果计数器运行多个线程我怎么能这样做...没有任何回调。所以我尝试使用Callable而不是Runnable,所以我正在使用Future,然后我得出结论WTH我在思考,未来将阻止它的future.get()方法所以我将最终结束是一种方法,与我原来的实现一样慢或慢。

所以这是我的道路,我现在有点被阻止,Runnable不能抛出异常,所以将计数器传递给任务也不会工作,Callable会阻塞,所以也没有选择。< / p>

如果任何多线程大师有一个想法,我将非常感激。下面是我最近的尝试,它没有被打破,但和我上面的方法一样慢。

  @Override
  @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Throwable.class)
  public void auditAllDomainConfigurationStatuses() throws InterruptedException, ExecutionException {
    logger.info("Starting audit of all domain configuration statusses");
    int errorStatusCounter = 0;
    Map<SubdomainRegistryStatus, List<String>> domainsByStatus = new HashMap<SubdomainRegistryStatus, List<String>>();
    List<DomainConfigurationStatus> domains = domainConfigurationStatusDao.findAll();

    for (DomainConfigurationStatus domainConfigurationStatus : domains) {      
      try {
        Future<Integer> futureResult = taskExecutor.submit(new DomainConfigurationAuditTask(errorStatusCounter, domainConfigurationStatusAuditor, domainConfigurationStatus.getId(), domainsByStatus, EMERGENCY_AUDIT_STOP_LIMIT));
        futureResult.get();
      }
      catch (Exception e) {
        logger.error("Emergency audit stop more then " + EMERGENCY_AUDIT_STOP_LIMIT + " records went into status ERROR");
        mailEmergencyDomainConfigurationStatusAuditStop();
        return;
      }            
    }

    mailDomainConfigurationStatusReport(domainsByStatus);
    logger.info("Audit of all domain configuration statusses completed");    
  }

1 个答案:

答案 0 :(得分:1)

这是一个非常简单的解决方案。基本上,完成某些工作(即DNS查找)的任务是完全隔离和可并行化的。成功或失败之后的部分工作是将成功布尔值提交给另一个{em>固定大小为1 的ExecutoService,这可以执行您想要的任何错误条件检查。

在这种情况下,它只是递增一个连续错误的整数,直到达到最大条件,然后设置一个错误条件,工作线程(DNS查找)全部检查首先是一个快速失败的方法,所以所有排队在满足错误条件后,任务将快速退出。

这样就可以非常简单地在多线程场景中跟踪连续的错误,就像你对响应进行单线程检查一样

我可以想到使用Java 8的CompletableFuture更优雅的解决方案,但听起来像是在桌面之外

package so.thread.errcondition;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {

  public static Random rand = new Random();

  public static ExecutorService workers = Executors.newFixedThreadPool(5);

  // NOTE, this executor has a FIXED size of 1 for in-order processing
  public static ExecutorService watcher = Executors.newFixedThreadPool(1);

  public static AtomicBoolean errorCondition = new AtomicBoolean(false);
  public static AtomicInteger errorCount = new AtomicInteger(0);

  public static Integer MAX_ERRORS = 5;

  public static void main(String[] args) throws Exception {


    int jobs = 1000;

    for (int i = 0; i < jobs; i++) {
      workers.submit(getWork());
    }

    Thread.sleep(TimeUnit.SECONDS.toMillis(5));

  }

  // parallelizable task, the number of parallel workers is irrelevant
  public static Runnable getWork() {
    return new Runnable() {
      @Override
      public void run() {

        // fail fast
        if (errorCondition.get()) {
          System.out.println("%%% MAX_ERRORS of [" + MAX_ERRORS + "] occurred, skipping task");
          return;
        }
        // do work
        if (rand.nextBoolean()) {
          // GOOD JOB
          System.out.println("+++ GOOD RESULT");
          submitDoneTask(true);
        } else {
          // ERROR
          System.out.println("*** BAD RESULT");
          submitDoneTask(false);
        }
      }
    };
  }

  public static void submitDoneTask(final boolean success) {
    watcher.submit(new Runnable() {
      @Override
      public void run() {
        if (!errorCondition.get() && success) {
          errorCount.set(0);
        } else {
          int errors = errorCount.incrementAndGet();
          if (errors >= MAX_ERRORS) {
            errorCondition.set(true);
          }
        }
      }
    });
  }
}