简体   繁体   中英

bounded ScheduledThreadPoolExecutor`s submit method don`t call ThreadFactory after release of a full queue

let corePoolSize = 4, after four calls to the submit(or scheduleAtFixedRate and etc) methods, query filled and the method getActiveCount() returns the correct value 4, after the cancellation working task through future.cancel(true) value decreases in getActiveCount() = 3, but new submit(or scheduleAtFixedRate and etc) don t call factory method Thread newThread(Runnable r) of ThreadFactory, before that was caused, and it s wrong i think, and getQueue() is zero after success submits, also never occur RejectedExecutionException without explicitly creating

public class ScheduledTaskCommandExecutor extends ScheduledThreadPoolExecutor {
  private static final TaskCommandThreadFactory factory;
  private static final ConcurrentSkipListMap<ScheduledFuture, String> activeTask;
  private final Semaphore semaphore;

  static {
    factory = new TaskCommandThreadFactory();
    activeTask = new ConcurrentSkipListMap<>();
  }

  public ScheduledTaskCommandExecutor(int corePoolSize) {
    super(4, factory, new RejectionHandler());
    //setMaximumPoolSize(corePoolSize);
    //allowCoreThreadTimeOut(true);
    semaphore = new Semaphore(corePoolSize);
    setRemoveOnCancelPolicy(true);
    setKeepAliveTime(10, TimeUnit.MILLISECONDS);
  }

  @Override
  protected void beforeExecute(Thread t, Runnable r) {
    super.beforeExecute(t, r);
    activeTask.putIfAbsent((ScheduledFuture) r, t.getName());
    System.out.println(t.getName() + " " + ConsoleProperties.Message.TASK_IS_READY.toString());
  }

  @Override
  protected void afterExecute(Runnable r, Throwable t) {
    try {
      long endTime = System.nanoTime();
    } finally {
      super.afterExecute(r, t);
      System.out.println(getTaskNameByFuture((ScheduledFuture) r) + " " + ConsoleProperties.Message.TASK_IS_COMPLETED.toString());
      activeTask.remove(r);
      //why getQueue()always empty after execute??
      for(Iterator<Runnable> iterator = getQueue().iterator();iterator.hasNext();) {
        System.out.println(iterator.toString());
      }
      //semaphore.release(1);
      purge();
    }
  }

  @Override
  protected void terminated() {
    try {

    } finally {
      super.terminated();
      System.out.println(ConsoleProperties.Message.EXECUTOR_TERMINATED);
    }
  }

  public TaskCommand execute(TaskCommand command) throws RejectedExecutionException {
    //semaphore.tryAcquire() tried that too
    if(getActiveCount() == getCorePoolSize()) {
      System.out.println(getActiveCount() + " " + getCorePoolSize());
      throw new RejectedExecutionException();
    }
    factory.setCommand(command);
    return command.setFuture(scheduleWithFixedDelay(command, command.getDelay(), command.getWaitInterval(), TimeUnit.MILLISECONDS));
  }

  @Override
  public ScheduledFuture<?> scheduleWithFixedDelay(Runnable runnable, long delay, long period, TimeUnit timeUnit) {
    try {
      return super.scheduleAtFixedRate(runnable, delay, period, timeUnit);
    } catch (RuntimeException e) {
      System.out.println("threadNumber:" + e);
      //todo log
      throw e;
    }
  }

  public boolean cancelTaskByName(String name) {
    if (activeTask.containsValue(name)) {
      for (Map.Entry<ScheduledFuture, String> entry : activeTask.entrySet()) {
        if (name.equals(entry.getValue())) {
          return entry.getKey().cancel(true);
        }
      }
    }
    return false;
  }

  public String getState() {
    StringBuilder ret = new StringBuilder();
    ret.append("task count:").append(getTaskCount()).append("\nactive count:").append(getActiveCount()).append("\n");
    ret.append(Arrays.deepToString(activeTask.values().toArray()) + "\n");
    for (Map.Entry<ScheduledFuture, String> entry : activeTask.entrySet()) {
      ret.append("task " + entry.getValue() + " is done " + entry.getKey().isDone() + "\n");
    }
    //ret.append(Arrays.deepToString(executor.getQueue().toArray()) + "\n");
    return ret.toString();
  }

  private String getTaskNameByFuture(ScheduledFuture task) {
    for (Map.Entry<ScheduledFuture, String> entry : activeTask.entrySet()) {
      if (entry.getKey() == task) {
        return entry.getValue();
      }
    }
    return ConsoleProperties.Error.TASK_NOT_FOUND.toString();
  }

  private static class TaskCommandThreadFactory implements ThreadFactory {
    static final AtomicInteger poolNumber = new AtomicInteger(1);
    final ThreadGroup group;
    AtomicInteger threadNumber = new AtomicInteger(1);
    final String namePrefix;
    private TaskCommand command;

    public void setCommand(TaskCommand command) {
      this.command = command;
    }

    TaskCommandThreadFactory() {
      SecurityManager s = System.getSecurityManager();
      group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
      namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
    }

    //this factory`s method don`t call after release full query
    @Override
    public Thread newThread(Runnable r) {
      int n = threadNumber.getAndIncrement();
      //System.out.println("threadNumber:" + n);
      command.setSerialNumber(n);
      Thread ret = new Thread(group, r, /*namePrefix +*/ command.getName(), 0);
      if (ret.isDaemon()) ret.setDaemon(false);
      if (ret.getPriority() != Thread.NORM_PRIORITY) ret.setPriority(Thread.NORM_PRIORITY);
      ret.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
          //todo log4j
          System.out.println(t.getName() + " : Error: " + e.getMessage());
        }
      });
      System.out.println("command thread number:" + command.getName());
      return ret;
    }
  }

  //and this will never occur this Exception without explicitly creating in  
  //public TaskCommand execute(TaskCommand command) method
  private static class RejectionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
      //todo log4j
      System.out.println(r.toString() + " : Rejected");
      throw new RejectedExecutionException();
    }
  }
}

somewhere in the code later

ScheduledTaskCommandExecutor exec = new ScheduledTaskCommandExecutor(2);
Future futureOne = exec.execute(Runnable); // called method public Thread newThread(Runnable r) in ThreadFactory
why getQueue().size() is zero ?
getActiveCount() is 1;

Future futureTwo = exec.execute(Runnable); // called public Thread newThread(Runnable r) in ThreadFactory
why getQueue().size() is zero ?
getActiveCount() is 2;

Future futureThree = exec.execute(Runnable); // error because checking in  getActiveCount() == getCorePoolSize(),

up to this point everything is correct

futureOne.cancel(true);
getQueue().size() is still zero;
getActiveCount() is 1;

//again
Future futureThree = exec.execute(Runnable); // why now method newThread(Runnable r) in ThreadFactory has not called ?

如果我理解了这个问题(这有点令人困惑),那么这是可取的行为-您拥有一个池化线程,因此您无需创建一个新线程,而执行者只是窥视空闲线程并重新使用它。

如果您只想钩住新任务的执行,请尝试覆盖beforeExecute方法。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM