PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/CommonControls/common/threads/WorkerThreadPool.java

#
Java | 260 lines | 160 code | 27 blank | 73 comment | 24 complexity | fcbe48d88b229782ebc483b0c696de63 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * WorkerThreadPool.java - a thread pool that handles groups of requests.
  3. * Copyright (c) 2005 Marcelo Vanzin
  4. *
  5. * :tabSize=4:indentSize=4:noTabs=false:maxLineLen=0:
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. */
  21. package common.threads;
  22. import java.util.Iterator;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import org.gjt.sp.util.Log;
  26. /** A thread pool that handles groups of requests (<i>Runnable</i> objects).
  27. *
  28. * <p>Users are encouraged to use the shared instance by calling
  29. * {@link #getSharedInstance()}, but they can instantiate new pools
  30. * if they so desire. Just remember to shut down the threads when
  31. * the plugin is unloaded.</p>
  32. *
  33. * <p>This is a version of Java 5's java.util.concurrent.ThreadPoolExecutor
  34. * with one addition:
  35. * addRequests() will start the runnables at the same time, or
  36. * wait until size threads are available in the pool.
  37. *
  38. * @author Marcelo Vanzin
  39. * @since CC 0.9.0
  40. * @see org.gjt.sp.util.ThreadUtilities
  41. */
  42. public class WorkerThreadPool
  43. {
  44. private static WorkerThreadPool instance = new WorkerThreadPool();
  45. public static WorkerThreadPool getSharedInstance() {
  46. return instance;
  47. }
  48. private List threads;
  49. private List requests = new LinkedList();
  50. private final Object lock = new Object();
  51. private final ThreadGroup group = new ThreadGroup("CommonControls Worker Pool");
  52. /**
  53. * Adds a request to the pool. The request will be run in the first
  54. * available thread after it becomes available. This method will not
  55. * block; it will just enqueue the request.
  56. *
  57. * <p> If no threads have yet been started, a new thread is created
  58. * and started.</p>
  59. */
  60. public WorkRequest addRequest(Runnable req)
  61. {
  62. ensureCapacity(1);
  63. WorkRequest wreq = new WorkRequest(req);
  64. synchronized (lock)
  65. {
  66. requests.add(wreq);
  67. lock.notifyAll();
  68. }
  69. return wreq;
  70. }
  71. /**
  72. * Makes sure that there are enough threads to execute all requests
  73. * in parallel and enqueue the requests. It's not guaranteed that all
  74. * the requests will run in parallel, but the pool is guaranteed to
  75. * have enough running threads to do so.
  76. *
  77. * <p>So if you enqueue 4 requests and there are 4 idle threads, each
  78. * request will run on a separate thread. But if one thread is running
  79. * a long request, it may happen that one of the new requests might
  80. * finish before that running job, and another one of the new requests
  81. * will run on that same thread.</p>
  82. */
  83. public WorkRequest[] addRequests(Runnable[] reqs)
  84. {
  85. ensureCapacity(reqs.length);
  86. WorkRequest[] wreqs = toWorkRequest(reqs);
  87. synchronized (lock)
  88. {
  89. for (int i = 0; i < wreqs.length; i++)
  90. requests.add(wreqs[i]);
  91. lock.notifyAll();
  92. }
  93. return wreqs;
  94. }
  95. /**
  96. * Immediately runs the given requests. If not enough worker threads
  97. * are free to handle all the requests, new threads are created to
  98. * be able to handle the new requests.
  99. *
  100. * @since CC 0.9.1
  101. */
  102. public WorkRequest[] runRequests(Runnable[] reqs)
  103. {
  104. WorkRequest[] wreqs = toWorkRequest(reqs);
  105. ensureCapacity(wreqs.length);
  106. synchronized (lock) {
  107. int curr = 0;
  108. for (Iterator i = threads.iterator();
  109. curr < wreqs.length && i.hasNext(); )
  110. {
  111. WorkerThread wt = (WorkerThread) i.next();
  112. if (wt.isIdle()) {
  113. wt.setWorkload(wreqs[curr++]);
  114. }
  115. }
  116. for (int i = curr; i < wreqs.length; i++) {
  117. WorkerThread t = new WorkerThread();
  118. t.setWorkload(wreqs[i]);
  119. t.start();
  120. threads.add(t);
  121. }
  122. lock.notifyAll();
  123. }
  124. return wreqs;
  125. }
  126. /**
  127. * Ensures that at least <code>size</code> threads are available to
  128. * handle requests.
  129. */
  130. public void ensureCapacity(int size) {
  131. synchronized (lock)
  132. {
  133. if (threads == null)
  134. {
  135. threads = new LinkedList();
  136. }
  137. while (threads.size() < size)
  138. {
  139. Thread t = new WorkerThread();
  140. t.start();
  141. threads.add(t);
  142. }
  143. }
  144. }
  145. /** Asks all running threads to shutdown. */
  146. public void shutdown()
  147. {
  148. synchronized (lock)
  149. {
  150. if (threads != null)
  151. {
  152. for (Iterator i = threads.iterator(); i.hasNext(); ) {
  153. ((WorkerThread)i.next()).requestShutdown();
  154. i.remove();
  155. }
  156. }
  157. }
  158. }
  159. private WorkRequest[] toWorkRequest(Runnable[] reqs) {
  160. WorkRequest[] wreqs = new WorkRequest[reqs.length];
  161. for (int i = 0; i < wreqs.length; i++)
  162. wreqs[i] = new WorkRequest(reqs[i]);
  163. return wreqs;
  164. }
  165. private static int THREAD_ID = 0;
  166. private class WorkerThread extends Thread
  167. {
  168. private boolean run = true;
  169. private int idleCount = 0;
  170. private volatile WorkRequest work = null;
  171. public WorkerThread() {
  172. super(group, "CC::Worker #" + (++THREAD_ID));
  173. setDaemon(true);
  174. }
  175. /** not synchronized. call while holding "lock". */
  176. public void setWorkload(WorkRequest work) {
  177. this.work = work;
  178. }
  179. /** not synchronized. call while holding "lock". */
  180. public boolean isIdle() {
  181. return (work == null);
  182. }
  183. public void run()
  184. {
  185. while (run)
  186. {
  187. idleCount = 0;
  188. synchronized (lock)
  189. {
  190. while (run && work == null && idleCount < 10)
  191. {
  192. if (requests.size() > 0)
  193. {
  194. work = (WorkRequest) requests.remove(0);
  195. break;
  196. }
  197. try {
  198. lock.wait(10000);
  199. } catch (InterruptedException ie) {
  200. // ignore.
  201. ie.printStackTrace();
  202. }
  203. idleCount++;
  204. }
  205. }
  206. if (work != null)
  207. {
  208. Log.log(Log.NOTICE, this, "Executing request: " + work.getRunnable());
  209. work.run();
  210. work = null;
  211. }
  212. else if (idleCount >= 10)
  213. {
  214. // stop the thread if if has been inactive for a long
  215. // time and there's more than 1 thread running
  216. synchronized (lock)
  217. {
  218. run = !(threads.size() > 1);
  219. if (!run)
  220. threads.remove(this);
  221. }
  222. }
  223. }
  224. }
  225. public void requestShutdown()
  226. {
  227. synchronized (lock) {
  228. run = false;
  229. lock.notifyAll();
  230. }
  231. }
  232. }
  233. }