content: package com.ctrip.framework.apollo.core.utils;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.Set;
  import java.util.concurrent.ThreadFactory;
  import java.util.concurrent.TimeUnit;
  import java.util.concurrent.atomic.AtomicLong;
  
  public class ApolloThreadFactory implements ThreadFactory {
    private static Logger log = LoggerFactory.getLogger(ApolloThreadFactory.class);
  
    private final AtomicLong threadNumber = new AtomicLong(1);
  
    private final String namePrefix;
  
    private final boolean daemon;
  
    private static final ThreadGroup threadGroup = new ThreadGroup("Apollo");
  
    public static ThreadGroup getThreadGroup() {
      return threadGroup;
    }
  
    public static ThreadFactory create(String namePrefix, boolean daemon) {
      return new ApolloThreadFactory(namePrefix, daemon);
    }
  
    public static boolean waitAllShutdown(int timeoutInMillis) {
      ThreadGroup group = getThreadGroup();
      Thread[] activeThreads = new Thread[group.activeCount()];
      group.enumerate(activeThreads);
      Set<Thread> alives = new HashSet<Thread>(Arrays.asList(activeThreads));
      Set<Thread> dies = new HashSet<Thread>();
      log.info("Current ACTIVE thread count is: {}", alives.size());
      long expire = System.currentTimeMillis() + timeoutInMillis;
      while (System.currentTimeMillis() < expire) {
        classify(alives, dies, new ClassifyStandard<Thread>() {
          @Override
          public boolean satisfy(Thread thread) {
            return !thread.isAlive() || thread.isInterrupted() || thread.isDaemon();
          }
        });
        if (alives.size() > 0) {
          log.info("Alive apollo threads: {}", alives);
          try {
            TimeUnit.SECONDS.sleep(2);
          } catch (InterruptedException ex) {
            // ignore
          }
        } else {
          log.info("All apollo threads are shutdown.");
          return true;
        }
      }
      log.warn("Some apollo threads are still alive but expire time has reached, alive threads: {}",
          alives);
      return false;
    }
  
    private static interface ClassifyStandard<T> {
      boolean satisfy(T thread);
    }
  
    private static <T> void classify(Set<T> src, Set<T> des, ClassifyStandard<T> standard) {
      Set<T> set = new HashSet<>();
      for (T t : src) {
        if (standard.satisfy(t)) {
          set.add(t);
        }
      }
      src.removeAll(set);
      des.addAll(set);
    }
  
    private ApolloThreadFactory(String namePrefix, boolean daemon) {
      this.namePrefix = namePrefix;
      this.daemon = daemon;
    }
  
    public Thread newThread(Runnable runnable) {
      Thread thread = new Thread(threadGroup, runnable,//
          threadGroup.getName() + "-" + namePrefix + "-" + threadNumber.getAndIncrement());
      thread.setDaemon(daemon);
      if (thread.getPriority() != Thread.NORM_PRIORITY) {
        thread.setPriority(Thread.NORM_PRIORITY);
      }
      return thread;
    }
  }
  