package com.austinv11.collectiveframework.multithreading; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Runnable class to ease the management of threads */ public abstract class SimpleRunnable implements Runnable { public volatile static boolean RESTRICT_THREAD_USAGE = true; //Key of true = the thread is being used, vice versa private volatile static ConcurrentHashMap<Boolean, ConcurrentHashMap<Integer, SimpleThread>> threadMap = new ConcurrentHashMap<Boolean, ConcurrentHashMap<Integer, SimpleThread>>(); private volatile boolean isCleaned = false; private volatile int thread = -1; private volatile long delay = -1; static { threadMap.put(true, new ConcurrentHashMap<Integer, SimpleThread>()); threadMap.put(false, new ConcurrentHashMap<Integer, SimpleThread>()); } /** * Disables this thread from running * @param clean Whether to clean the thread running - This will prevent this runnable from being able to run, only cleans if enabled in the config * @throws IllegalThreadStateException */ public final void disable(boolean clean) throws IllegalThreadStateException { if (isCleaned) throw new IllegalThreadStateException("Thread "+this.getName()+" is cleaned already!"); getThread().isActive = false; getThread().delay = -1; getThread().started = false; if (clean && RESTRICT_THREAD_USAGE) { isCleaned = true; add(threadMap.get(false), getThread()); threadMap.get(true).remove(thread); } } /** * Attempts to reenable this thread * @throws IllegalThreadStateException */ public final void enable() throws IllegalThreadStateException { if (isCleaned) throw new IllegalThreadStateException("Thread "+this.getName()+" is cleaned already!"); getThread().isActive = true; getThread().delay = delay; getThread().started = true; } /** * Runs a thread with this runnable */ public final void start() { getThread().start(); } /** * Called by the thread to run this runnable */ @Override public abstract void run(); /** * Gets the name of the thread * @return The name */ public String getName() { return this.getClass().getSimpleName(); } /** * Delays the given thread from running * @param delay The length of the delay (in milliseconds) */ public final void delay(int delay) throws InterruptedException { getThread().wait(delay); } /** * Gets whether is runnable is cleaned * @return IsCleaned */ public boolean isCleaned() { return isCleaned; } /** * Gets whether the runnable is running * @return IsEnabled */ public boolean isEnabled() { return getThread().isActive; } /** * Makes the thread loop every x milliseconds * @param delay The delay for each loop, in milliseconds */ public void setTicking(long delay) { this.delay = delay; getThread().delay = delay; } /** * Gets the internal thread running the runnable * @return The thread {@link com.austinv11.collectiveframework.multithreading.SimpleThread} */ private final SimpleThread getThread() { if (this.thread == -1) { //Assign a thread if (threadMap.get(false).size() < 1) { SimpleThread thread = new SimpleThread(this, this.getName()); this.thread = add(threadMap.get(true), thread); return getThread(); } else { Map.Entry<Integer, SimpleThread> entry = threadMap.get(false).entrySet().iterator().next(); SimpleThread thread = entry.getValue(); threadMap.get(false).remove(entry.getKey()); thread.isActive = true; thread.setName(getName()); try { thread.setTarget(this); this.thread = add(threadMap.get(true), thread); return getThread(); } catch (Exception e) { e.printStackTrace(); } } } else { //Fetch the thread return threadMap.get(true).get(this.thread); } return null; //This should never be reached } private static <T> int add(Map<Integer, T> map, T o) { int i = 0; while (map.containsKey(i)) i++; map.put(i, o); return i; } @Override public boolean equals(Object other) { if (other instanceof SimpleRunnable) return ((SimpleRunnable) other).thread == thread && ((SimpleRunnable) other).getName().equals(getName()); return false; } @Override public String toString() { return "SimpleRunnable(Name: "+getName()+" ID:"+thread+" Is Cleaned: "+isCleaned+")"; } }