All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.unidal.helper.Threads Maven / Gradle / Ivy

The newest version!
package org.unidal.helper;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

import org.unidal.lookup.annotation.Named;
import org.unidal.lookup.logging.LogEnabled;
import org.unidal.lookup.logging.Logger;
import org.unidal.lookup.logging.TimedConsoleLoggerManager;

public class Threads {
   private static volatile Manager s_manager = new Manager();

   public static void addListener(ThreadListener listener) {
      s_manager.addListener(listener);
   }

   public static ThreadGroupManager forGroup() {
      return s_manager.getThreadGroupManager("Background");
   }

   public static ThreadGroupManager forGroup(String name) {
      return s_manager.getThreadGroupManager(name);
   }

   public static ThreadPoolManager forPool() {
      return s_manager.getThreadPoolManager();
   }

   public static String getCallerClass() {
      return RunnableThread.m_callerThreadLocal.get();
   }

   public static void removeListener(ThreadListener listener) {
      s_manager.removeListener(listener);
   }

   public static void reset() {
      s_manager.reset();
   }

   /**
    * Sleep for a total timeoutInMillis milli-seconds while when is null or
    * true.
    * 
    * @param when
    *           optional. true to make sleep happen, false to break
    * @param timeoutInMillis
    *           max time to sleep if when condition is null or met
    * @throws InterruptedException
    */
   public static void sleep(AtomicBoolean when, long timeoutInMillis) throws InterruptedException {
      if (when != null && !when.get() || timeoutInMillis <= 0) {
         return;
      }

      long deadline = System.currentTimeMillis() + timeoutInMillis;

      while (when == null || when.get()) {
         TimeUnit.MILLISECONDS.sleep(1);

         if (System.currentTimeMillis() >= deadline) {
            break;
         }
      }
   }

   public static abstract class AbstractThreadListener implements ThreadListener {
      @Override
      public void onThreadGroupCreated(ThreadGroup group, String name) {
         // to be override
      }

      @Override
      public void onThreadPoolCreated(ExecutorService pool, String name) {
         // to be override
      }

      @Override
      public void onThreadStarting(Thread thread, String name) {
         // to be override
      }

      @Override
      public void onThreadStopping(Thread thread, String name) {
         // to be override
      }

      @Override
      public boolean onUncaughtException(Thread thread, Throwable e) {
         // to be override
         return false;
      }
   }

   static class DefaultThreadFactory implements ThreadFactory {
      private ThreadGroup m_threadGroup;

      private String m_name;

      private AtomicInteger m_index = new AtomicInteger();

      private UncaughtExceptionHandler m_handler;

      public DefaultThreadFactory(String name) {
         m_threadGroup = new ThreadGroup(name);
         m_name = name;
      }

      public DefaultThreadFactory(ThreadGroup threadGroup) {
         m_threadGroup = threadGroup;
         m_name = threadGroup.getName();
      }

      public String getName() {
         return m_name;
      }

      @Override
      public Thread newThread(Runnable r) {
         int nextIndex = m_index.getAndIncrement(); // always increase by one
         String threadName;

         if (r instanceof Task) {
            threadName = m_name + "-" + ((Task) r).getName();
         } else {
            threadName = m_name + "-" + nextIndex;
         }

         return new RunnableThread(m_threadGroup, r, threadName, m_handler);
      }

      public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
         m_handler = handler;
      }
   }

   @Named(type = ThreadListener.class, value = "logger")
   public static class LoggerThreadListener implements ThreadListener, LogEnabled {
      private Logger m_logger;

      public LoggerThreadListener() {
      }

      public LoggerThreadListener(Logger logger) {
         m_logger = logger;
      }

      @Override
      public void enableLogging(Logger logger) {
         m_logger = logger;
      }

      @Override
      public void onThreadGroupCreated(ThreadGroup group, String name) {
         m_logger.info(String.format("Thread group(%s) created.", name));
      }

      @Override
      public void onThreadPoolCreated(ExecutorService pool, String name) {
         m_logger.info(String.format("Thread pool(%s) created.", name));
      }

      @Override
      public void onThreadStarting(Thread thread, String name) {
         m_logger.info(String.format("Starting thread(%s) ...", name));
      }

      @Override
      public void onThreadStopping(Thread thread, String name) {
         m_logger.info(String.format("Stopping thread(%s).", name));
      }

      @Override
      public boolean onUncaughtException(Thread thread, Throwable e) {
         m_logger.error(String.format("Uncaught exception thrown out of thread(%s)!", thread.getName()), e);
         return true;
      }
   }

   static class Manager implements UncaughtExceptionHandler {
      private Map m_groupManagers = new LinkedHashMap();

      private List m_listeners = new ArrayList();

      private ThreadPoolManager m_threadPoolManager;

      public Manager() {
         TimedConsoleLoggerManager.skipClass(getClass());

         Thread shutdownThread = new Thread() {
            @Override
            public void run() {
               shutdownAll();
            }
         };

         m_threadPoolManager = new ThreadPoolManager(this);
         shutdownThread.setDaemon(true);

         try {
            Runtime.getRuntime().addShutdownHook(shutdownThread);
         } catch (Throwable e) {
            // in case of system shutting down, runtime could be null
         }
      }

      public void reset() {
         for (ThreadGroupManager groupManager : m_groupManagers.values()) {
            groupManager.shutdown();
         }

         m_groupManagers.clear();
         m_listeners.clear();
      }

      public void addListener(ThreadListener listener) {
         m_listeners.add(listener);
      }

      public ThreadGroupManager getThreadGroupManager(String name) {
         ThreadGroupManager groupManager = m_groupManagers.get(name);

         if (groupManager == null) {
            synchronized (this) {
               groupManager = m_groupManagers.get(name);

               if (groupManager != null && !groupManager.isActive()) {
                  m_groupManagers.remove(name);
                  groupManager = null;
               }

               if (groupManager == null) {
                  groupManager = new ThreadGroupManager(this, name);
                  m_groupManagers.put(name, groupManager);

                  onThreadGroupCreated(groupManager.getThreadGroup(), name);
               }
            }
         }

         return groupManager;
      }

      public ThreadPoolManager getThreadPoolManager() {
         return m_threadPoolManager;
      }

      public void onThreadGroupCreated(ThreadGroup group, String name) {
         for (ThreadListener listener : m_listeners) {
            try {
               listener.onThreadGroupCreated(group, name);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }

      public void onThreadPoolCreated(ExecutorService service, String name) {
         for (ThreadListener listener : m_listeners) {
            try {
               listener.onThreadPoolCreated(service, name);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }

      public void onThreadStarting(Thread thread, String name) {
         for (ThreadListener listener : m_listeners) {
            try {
               listener.onThreadStarting(thread, name);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }

      public void onThreadStopped(Thread thread, String name) {
         for (ThreadListener listener : m_listeners) {
            try {
               listener.onThreadStopping(thread, name);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }

      public void removeListener(ThreadListener listener) {
         m_listeners.remove(listener);
      }

      public void shutdownAll() {
         try {
            for (ThreadGroupManager manager : m_groupManagers.values()) {
               manager.shutdown();
            }

            m_threadPoolManager.shutdownAll();
         } catch (Throwable e) {
            // all exceptions during shutdown are ignored
         }
      }

      @Override
      public void uncaughtException(Thread thread, Throwable e) {
         for (ThreadListener listener : m_listeners) {
            boolean handled = listener.onUncaughtException(thread, e);

            if (handled) {
               break;
            }
         }
      }
   }

   static class RunnableThread extends Thread {
      private static ThreadLocal m_callerThreadLocal = new ThreadLocal();

      private Runnable m_target;

      private String m_caller;

      private CountDownLatch m_latch = new CountDownLatch(1);

      public RunnableThread(ThreadGroup threadGroup, Runnable target, String name, UncaughtExceptionHandler handler) {
         super(threadGroup, target, name);

         m_target = target;
         m_caller = getCaller();

         setDaemon(true);
         setUncaughtExceptionHandler(handler);

         if (getPriority() != Thread.NORM_PRIORITY) {
            setPriority(Thread.NORM_PRIORITY);
         }
      }

      private String getCaller() {
         StackTraceElement[] elements = new Exception().getStackTrace();
         String prefix = Threads.class.getName() + "$";

         for (int i = 0; i < elements.length; i++) {
            String className = elements[i].getClassName();

            if (className.startsWith(prefix)) {
               continue;
            }

            int pos = className.lastIndexOf('$');

            if (pos < 0) {
               pos = className.lastIndexOf('.');
            }

            if (pos < 0) {
               return className;
            } else {
               return className.substring(pos + 1);
            }
         }

         return null;
      }

      public Runnable getTarget() {
         return m_target;
      }

      public void await() throws InterruptedException {
         m_latch.await();
      }

      @Override
      public void run() {
         m_callerThreadLocal.set(m_caller);
         s_manager.onThreadStarting(this, getName());
         m_latch.countDown();
         super.run();
         s_manager.onThreadStopped(this, getName());
         m_callerThreadLocal.remove();
      }

      public void shutdown() {
         if (m_target instanceof Task) {
            ((Task) m_target).shutdown();
         } else {
            System.out.println(String.format("Thread(%s) is shutdown! ", getName()));
            interrupt();
         }
      }
   }

   public static interface Task extends Runnable {
      public String getName();

      public void shutdown();
   }

   public static class ThreadGroupManager {
      private DefaultThreadFactory m_factory;

      private ThreadGroup m_threadGroup;

      private boolean m_active;

      private boolean m_deamon;

      public ThreadGroupManager(UncaughtExceptionHandler handler, String name) {
         m_threadGroup = new ThreadGroup(name);
         m_factory = new DefaultThreadFactory(m_threadGroup);
         m_factory.setUncaughtExceptionHandler(handler);
         m_active = true;
         m_deamon = true;
      }

      public void awaitTermination(long time, TimeUnit unit) {
         long remaining = unit.toNanos(time);

         while (remaining > 0) {
            int len = m_threadGroup.activeCount();
            Thread[] activeThreads = new Thread[len];
            int num = m_threadGroup.enumerate(activeThreads);
            boolean anyAlive = false;

            for (int i = 0; i < num; i++) {
               Thread thread = activeThreads[i];

               if (thread.isAlive()) {
                  anyAlive = true;
                  break;
               }
            }

            if (anyAlive) {
               long slice = 1000 * 1000L;

               // wait for 1 ms
               LockSupport.parkNanos(slice);
               remaining -= slice;
            } else {
               break;
            }
         }
      }

      public ThreadGroup getThreadGroup() {
         return m_threadGroup;
      }

      public boolean isActive() {
         return m_active;
      }

      public ThreadGroupManager nonDaemon() {
         m_deamon = false;
         return this;
      }

      public void shutdown() {
         int len = m_threadGroup.activeCount();
         Thread[] activeThreads = new Thread[len];
         int num = m_threadGroup.enumerate(activeThreads);

         for (int i = 0; i < num; i++) {
            Thread thread = activeThreads[i];

            if (thread instanceof RunnableThread) {
               ((RunnableThread) thread).shutdown();
            } else if (thread.isAlive()) {
               thread.interrupt();
            }
         }

         m_active = false;
      }

      public Thread start(Runnable runnable) {
         return start(runnable, m_deamon);
      }

      public Thread start(Runnable runnable, boolean deamon) {
         Thread thread = m_factory.newThread(runnable);

         thread.setDaemon(deamon);
         thread.start();

         if (thread instanceof RunnableThread) {
            try {
               ((RunnableThread) thread).await();
            } catch (InterruptedException e) {
               // ignore it
            }
         }

         return thread;
      }
   }

   public static interface ThreadListener {
      public void onThreadGroupCreated(ThreadGroup group, String name);

      /**
       * Triggered when a thread pool (ExecutorService) has been created.
       * 
       * @param pool
       *           thread pool
       * @param pattern
       *           thread pool name pattern
       */
      public void onThreadPoolCreated(ExecutorService pool, String pattern);

      /**
       * Triggered when a thread is starting.
       * 
       * @param thread
       *           thread which is starting
       * @param name
       *           thread name
       */
      public void onThreadStarting(Thread thread, String name);

      public void onThreadStopping(Thread thread, String name);

      /**
       * Triggered when an uncaught exception thrown from within a thread.
       * 
       * @param thread
       *           thread which has an uncaught exception thrown
       * @param e
       *           the exception uncaught
       * @return true means the exception is handled, it will be not handled again other listeners, false otherwise.
       */
      public boolean onUncaughtException(Thread thread, Throwable e);
   }

   public static class ThreadPoolManager {
      private UncaughtExceptionHandler m_handler;

      private Map m_services = new LinkedHashMap();

      public ThreadPoolManager(UncaughtExceptionHandler handler) {
         m_handler = handler;
      }

      public ExecutorService getCachedThreadPool(String name) {
         ExecutorService service = m_services.get(name);

         if (service != null && service.isShutdown()) {
            m_services.remove(name);
            service = null;
         }

         if (service == null) {
            synchronized (this) {
               service = m_services.get(name);

               if (service == null) {
                  DefaultThreadFactory factory = newThreadFactory(name);
                  service = Executors.newCachedThreadPool(factory);

                  m_services.put(name, service);
                  s_manager.onThreadPoolCreated(service, factory.getName());
               }
            }
         }

         return service;
      }

      public ExecutorService getFixedThreadPool(String name, int nThreads) {
         ExecutorService service = m_services.get(name);

         if (service != null && service.isShutdown()) {
            m_services.remove(name);
            service = null;
         }

         if (service == null) {
            synchronized (this) {
               service = m_services.get(name);

               if (service == null) {
                  DefaultThreadFactory factory = newThreadFactory(name);
                  service = Executors.newFixedThreadPool(nThreads, factory);

                  m_services.put(name, service);
                  s_manager.onThreadPoolCreated(service, factory.getName());
               }
            }
         }

         return service;
      }

      public ScheduledExecutorService getScheduledThreadPool(String name, int nThreads) {
         ExecutorService service = m_services.get(name);

         if (service != null && service.isShutdown()) {
            m_services.remove(name);
            service = null;
         }

         if (service == null) {
            synchronized (this) {
               service = m_services.get(name);

               if (service == null) {
                  DefaultThreadFactory factory = newThreadFactory(name);
                  service = Executors.newScheduledThreadPool(nThreads, factory);

                  m_services.put(name, service);
                  s_manager.onThreadPoolCreated(service, factory.getName());
               }
            }
         }

         return (ScheduledExecutorService) service;
      }

      DefaultThreadFactory newThreadFactory(String name) {
         DefaultThreadFactory factory = new DefaultThreadFactory(name);

         factory.setUncaughtExceptionHandler(m_handler);
         return factory;
      }

      public void shutdownAll() {
         for (ExecutorService service : m_services.values()) {
            service.shutdown();
         }

         boolean allTerminated = false;
         int count = 100;

         while (!allTerminated && count-- > 0) {
            try {
               for (ExecutorService service : m_services.values()) {
                  if (!service.isTerminated()) {
                     service.awaitTermination(10, TimeUnit.MILLISECONDS);
                  }
               }

               allTerminated = true;
            } catch (InterruptedException e) {
               // ignore it
            }
         }

         m_services.clear();
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy