PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/concurrent/java/ThreadPool.java

https://bitbucket.org/bedlaczech/fan-1.0
Java | 348 lines | 187 code | 42 blank | 119 comment | 39 complexity | ec7021cd6d3844f8573c08d76fc786d1 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //
  2. // Copyright (c) 2009, Brian Frank and Andy Frank
  3. // Licensed under the Academic Free License version 3.0
  4. //
  5. // History:
  6. // 27 Mar 09 Brian Frank Creation
  7. //
  8. package fan.concurrent;
  9. import java.util.LinkedList;
  10. import java.util.HashMap;
  11. import java.util.Iterator;
  12. /**
  13. * ThreadPool manages a pool of threads optimized for the Actor framework.
  14. */
  15. public class ThreadPool
  16. {
  17. //////////////////////////////////////////////////////////////////////////
  18. // Constructor
  19. //////////////////////////////////////////////////////////////////////////
  20. /**
  21. * Construct with max number of threads.
  22. */
  23. public ThreadPool(String name, int max)
  24. {
  25. this.name = name;
  26. this.max = max;
  27. this.idleTime = 5000; // 5sec
  28. this.idle = new LinkedList();
  29. this.pending = new LinkedList();
  30. this.workers = new HashMap(max*3);
  31. this.state = RUNNING;
  32. }
  33. //////////////////////////////////////////////////////////////////////////
  34. // Lifecycle
  35. //////////////////////////////////////////////////////////////////////////
  36. /**
  37. * Has this pool been stopped or killed.
  38. */
  39. public final boolean isStopped()
  40. {
  41. return state != RUNNING;
  42. }
  43. /**
  44. * Has all the work in this queue finished processing and
  45. * all threads terminated.
  46. */
  47. public final boolean isDone()
  48. {
  49. if (state == DONE) return true;
  50. synchronized (this)
  51. {
  52. if (state == RUNNING || workers.size() > 0) return false;
  53. state = DONE;
  54. return true;
  55. }
  56. }
  57. /**
  58. * Orderly shutdown of threads. All pending work items are processed.
  59. */
  60. public final synchronized void stop()
  61. {
  62. state = STOPPING;
  63. // immediately wake up all the idle workers so they can die
  64. while (true)
  65. {
  66. Worker w = (Worker)idle.poll();
  67. if (w == null) break;
  68. w.post(null);
  69. }
  70. }
  71. /**
  72. * Unorderly shutdown of threads. All pending work are discarded,
  73. * and interrupt is sent to each thread.
  74. */
  75. public final synchronized void kill()
  76. {
  77. state = STOPPING;
  78. // kill all the pending work
  79. while (true)
  80. {
  81. Work work = (Work)pending.poll();
  82. if (work == null) break;
  83. work._kill();
  84. }
  85. // interupt each thread
  86. Iterator it = workers.values().iterator();
  87. while (it.hasNext()) ((Worker)it.next()).interrupt();
  88. }
  89. /**
  90. * Wait for all threads to stop.
  91. ** Return true on success or false on timeout.
  92. */
  93. public final synchronized boolean join(long msTimeout)
  94. throws InterruptedException
  95. {
  96. long deadline = System.nanoTime()/1000000L + msTimeout;
  97. while (true)
  98. {
  99. // if all workers have completed, then return success
  100. if (workers.size() == 0) return true;
  101. // if we have gone past our deadline, return false
  102. long toSleep = deadline - System.nanoTime()/1000000L;
  103. if (toSleep <= 0) return false;
  104. // sleep until something interesting happens
  105. wait(toSleep);
  106. }
  107. }
  108. //////////////////////////////////////////////////////////////////////////
  109. // Work Management
  110. //////////////////////////////////////////////////////////////////////////
  111. /**
  112. * Submit the given work to be run by a thread in this pool.
  113. * If an idle thread is available, the work is immediately
  114. * run. If no idle threads are available, but the current number
  115. * of threads is less than max, then launch a new thread to
  116. * execute the work. If the current number of threads is at
  117. * max, then queue the work until a thread becomes available.
  118. */
  119. public synchronized void submit(Work work)
  120. {
  121. // if we have an idle thread, use it
  122. Worker worker = (Worker)idle.poll();
  123. if (worker != null)
  124. {
  125. worker.post(work);
  126. return;
  127. }
  128. // if we are below max, then spawn a new thread
  129. if (workers.size() < max)
  130. {
  131. worker = new Worker(name + "-Worker-" + (counter++), work);
  132. worker.start();
  133. workers.put(worker, worker);
  134. return;
  135. }
  136. // queue the runnable until we have an idle thread
  137. pending.addLast(work);
  138. }
  139. /**
  140. * This is called by a worker when it completes a work item. If
  141. * there is pending work post it back to the worker and return true.
  142. * If there is no pending work and we are stopping then return
  143. * false, otherwise add worker to our idle queue and return true.
  144. */
  145. synchronized boolean ready(Worker w)
  146. {
  147. // if we have a pending work, then immediately reuse the worker
  148. Work work = (Work)pending.poll();
  149. if (work != null)
  150. {
  151. w.post(work);
  152. return true;
  153. }
  154. // if shutting down, then free the worker return false
  155. if (state != RUNNING)
  156. {
  157. free(w);
  158. return false;
  159. }
  160. // add to head of idle list (we let oldest threads die out first)
  161. idle.addFirst(w);
  162. return true;
  163. }
  164. /**
  165. * Free worker from all data structures and let it die.
  166. */
  167. synchronized void free(Worker w)
  168. {
  169. idle.remove(w);
  170. workers.remove(w);
  171. notifyAll();
  172. }
  173. //////////////////////////////////////////////////////////////////////////
  174. // Debug
  175. //////////////////////////////////////////////////////////////////////////
  176. public void dump(fan.sys.List args)
  177. {
  178. fan.sys.OutStream out = fan.sys.Env.cur().out();
  179. if (args != null && args.size() > 0)
  180. out = (fan.sys.OutStream)args.get(0);
  181. out.printLine("ThreadPool");
  182. out.printLine(" pending: " + pending.size());
  183. out.printLine(" idle: " + idle.size());
  184. out.printLine(" workers: " + workers.size());
  185. Iterator it = workers.values().iterator();
  186. while (it.hasNext())
  187. {
  188. Worker w = (Worker)it.next();
  189. out.printLine(" " + w + " " + w.work);
  190. }
  191. }
  192. //////////////////////////////////////////////////////////////////////////
  193. // Worker
  194. //////////////////////////////////////////////////////////////////////////
  195. /**
  196. * Worker is a reusable thread within the thread pool.
  197. */
  198. class Worker extends Thread
  199. {
  200. /**
  201. * Construct with name and initial work to execute.
  202. */
  203. Worker(String name, Work work)
  204. {
  205. super(name);
  206. this.work = work;
  207. }
  208. /**
  209. * Equality must be reference for storage in a hash table.
  210. */
  211. public final boolean equals(Object o)
  212. {
  213. return this == o;
  214. }
  215. /**
  216. * A worker thread loops repeatly executing work until it times out.
  217. */
  218. public void run()
  219. {
  220. try
  221. {
  222. // loop until we have explicit return
  223. while (true)
  224. {
  225. // execute work posted to me
  226. try { work._work(); } catch (Throwable e) { e.printStackTrace(); }
  227. work = null;
  228. // inform pool I'm ready for more work, three potential outcomes:
  229. // - if ready returns false then time to immediately
  230. // exit and have this thread die
  231. // - if ready posted a new work item to me, then continue
  232. // my loop and immediately execute it
  233. // - enter the idle state and wait for a bit more work
  234. if (!ready(this)) return;
  235. if (work != null) continue;
  236. // enter idle state until more work is posted to me
  237. synchronized(this)
  238. {
  239. // it is possible that between ready and acquiring my
  240. // lock that submit posted work to me, so double check
  241. // work field before I enter my sleep cycle
  242. if (work != null) continue;
  243. // enter wait state until either timeout or more work is posted
  244. try { wait(idleTime); } catch (InterruptedException e) {}
  245. if (work != null) continue;
  246. }
  247. // if we've made it here, then we've expired our idle time;
  248. // so free ourselves from the thread pool
  249. free(this);
  250. // it is possible that between releasing my lock and calling
  251. // free that submit posted one more work item to me, so double
  252. // check work field before we exit the thread
  253. synchronized (this)
  254. {
  255. if (work != null)
  256. {
  257. try { work._work(); } catch (Throwable e) { e.printStackTrace(); }
  258. }
  259. return;
  260. }
  261. }
  262. }
  263. catch (Throwable e)
  264. {
  265. // if an exception is raised, free worker
  266. e.printStackTrace();
  267. free(this);
  268. }
  269. }
  270. /**
  271. * Give this thread a work item and call notify in case its idling.
  272. */
  273. public synchronized void post(Work work)
  274. {
  275. this.work = work;
  276. notifyAll();
  277. }
  278. Work work;
  279. }
  280. //////////////////////////////////////////////////////////////////////////
  281. // Work
  282. //////////////////////////////////////////////////////////////////////////
  283. /**
  284. * Item of work to execute in the thread pool.
  285. * Note: method _work() is used so we don't polluate Actor's namespace.
  286. */
  287. public static interface Work
  288. {
  289. public void _work();
  290. public void _kill();
  291. }
  292. //////////////////////////////////////////////////////////////////////////
  293. // Fields
  294. //////////////////////////////////////////////////////////////////////////
  295. static final int RUNNING = 0;
  296. static final int STOPPING = 1;
  297. static final int DONE = 2;
  298. final String name; // actor pool name
  299. final int max; // maximum number of threads to use
  300. final int idleTime; // time in ms to let threads idle (5sec)
  301. private volatile int state; // life cycle state
  302. private LinkedList idle; // idle threads waiting for work
  303. private LinkedList pending; // pending working we don't have threads for yet
  304. private HashMap workers; // map of all worker threads
  305. private int counter; // counter for all threads ever created
  306. }