如何使用预先存在的runnables,限制要创建的runnables的数量。

时间:2014-08-27 07:15:18

标签: java multithreading threadpool runnable executorservice

问题陈述:

  1. 我有5000个id指向数据库中的行。[可能超过5000]

  2. 每个Runnable在给定id的情况下检索数据库中的行并执行一些耗时的任务

    public class BORunnable implements Callable<Properties>{
    
      public BORunnable(String branchID) {            
        this.branchID=branchID;            
      }
    
      public setBranchId(String branchID){
        this.branchID=branchID;
      }
      public Properties call(){
        //Get the branchID
        //Do some time consuming tasks. Merely takes 1 sec to complete
    
        return propObj;            
      }
    }
    
  3. 我打算将这些runnable提交给执行者服务。

  4. 为此,我需要为执行程序服务创建并提交5000个甚至更多的runnable。在我的环境中创建runnable可能会抛出内存异常。 [鉴于5000只是一个例子]

  5. 所以我想出了一个方法,如果你提供不同的东西,我将感激不尽:

    1. 创建了固定大小为10的线程池。

      int corePoolSize = 10;
      ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, 
          corePoolSize + 5, 10, TimeUnit.SECONDS, 
          new LinkedBlockingQueue<Runnable>());
      
      Collection<Future<Properties>> futuresCollection = 
          new LinkedList<Future<Properties>>();
      
    2. 将所有branchID添加到branchIdQueue

      Queue<String> branchIdQueue = new LinkedList<String>();
      Collections.addAll(branchIdQueue, branchIDs);
      
    3. 我正在尝试重用runnable。创建了一堆可运行的

      现在我想要将这些元素出列并为每个

      创建runnable
      int noOfElementsToDequeue = Math.min(corePoolSize, branchIdQueue.size());
      
      ArrayList<BORunnable>runnablesList = dequeueAndSubmitRunnable(
          branchIdQueue,noOfElementsToDequeue);
      
      
      ArrayList<BORunnable> dequeueAndSubmitRunnable(branchIdQueue,
          noOFElementsToDequeue){
      ArrayList<BORunnable> runnablesList= new ArrayList<BORunnable>();
          for (int i = 0; i < noOfElementsToDequeue; i++) {
              //Create this number of runnables
              runnablesList.add(new BORunnable(branchIdQueue.remove()));
          }
      return runnablesList;
      }
      
    4. 将检索到的runnables提交给执行者

      for(BORunnable boRunnableObj:runnablesList){
           futuresCollection.add(executor.submit(boRunnableObj));
      }
      
    5. 如果队列为空,我创建了我需要的runnables。如果不是,我想重用runnable并提交给执行者。

    6. 在这里,我获得了要重用的可运行数量=总计数 - 当前活动计数 [近似对我来说足够了]

      int coreSize=executor.getCorePoolSize();
      
      while(!branchIdQueue.isEmpty()){
      
          //Total size - current active count 
          int runnablesToBeReused=coreSize-executor.getActiveCount();
          if(runnablesToBeReused!=0){
              ArrayList<String> branchIDsTobeReset = removeElementsFromQueue(
                  branchIdQueue,runnablesToBeReused);
              ArrayList<BORunnable> boRunnableToBeReusedList = 
                  getBORunnableToBeReused(boRunnableList,runnablesToBeReused);
              for(BORunnable aRunnable:boRunnableList){
                  //aRunnable.set(branchIDSTobeRest.get(0));
              }
          }
      
      }
      
    7. 我的问题是

      1. 我无法找出线程池已发布哪个Runnable,所以我可以使用它来提交

      2. 因此,我随机取几个runnable并尝试设置branchId,但随后可能会发生线程争用问题。 [不想使用volatile]

1 个答案:

答案 0 :(得分:1)

重用Runnable毫无意义,因为问题不在于创建或释放可运行实例的成本。这些在Java中几乎是免费的。

您要做的是限制易于实现的待处理作业数:只需向传递给执行程序服务的队列提供限制 。这就像将int值(限制)传递给LinkedBlockingQueue’s constructor一样简单。请注意,您也可以使用ArrayBlockingQueue,因为LinkedBlockingQueue不会为有界队列使用提供优势。

当您为队列提供限制时,执行程序将拒绝排队新作业。剩下要做的唯一事情就是向执行者提供适当的RejectedExecutionHandler。例如。 CallerRunsPolicy足以避免调用者在线程全部忙且队列已满时创建更多新作业。

执行后,Runnable将进行垃圾回收。