PageRenderTime 54ms CodeModel.GetById 20ms app.highlight 16ms RepoModel.GetById 14ms app.codeStats 1ms

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

#
Java | 260 lines | 160 code | 27 blank | 73 comment | 24 complexity | fcbe48d88b229782ebc483b0c696de63 MD5 | raw file
  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 */
 21package common.threads;
 22
 23import java.util.Iterator;
 24import java.util.LinkedList;
 25import java.util.List;
 26
 27import org.gjt.sp.util.Log;
 28
 29/** A thread pool that handles groups of requests (<i>Runnable</i> objects).
 30 *
 31 *	<p>Users are encouraged to use the shared instance by calling
 32 *	{@link #getSharedInstance()}, but they can instantiate new pools
 33 *	if they so desire. Just remember to shut down the threads when
 34 *	the plugin is unloaded.</p>
 35 *
 36 *	<p>This is a version of Java 5's java.util.concurrent.ThreadPoolExecutor
 37 *	with one addition:
 38 *     addRequests() will start the runnables at the same time, or
 39 *        wait until size threads are available in the pool.
 40 *
 41 *	@author		Marcelo Vanzin
 42 *	@since		CC 0.9.0
 43 *  @see org.gjt.sp.util.ThreadUtilities
 44 */
 45
 46public class WorkerThreadPool
 47{
 48
 49	private static WorkerThreadPool instance = new WorkerThreadPool();
 50
 51	public static WorkerThreadPool getSharedInstance() {
 52		return instance;
 53	}
 54
 55	private 		List		threads;
 56	private 		List		requests	= new LinkedList();
 57
 58	private final 	Object		lock		= new Object();
 59	private final 	ThreadGroup	group		= new ThreadGroup("CommonControls Worker Pool");
 60
 61	/**
 62	 *	Adds a request to the pool. The request will be run in the first
 63	 *	available thread after it becomes available. This method will not
 64	 *	block; it will just enqueue the request.
 65	 *
 66	 *	<p> If no threads have yet been started, a new thread is created
 67	 *	and started.</p>
 68	 */
 69	public WorkRequest addRequest(Runnable req)
 70	{
 71		ensureCapacity(1);
 72		WorkRequest wreq = new WorkRequest(req);
 73		synchronized (lock)
 74		{
 75			requests.add(wreq);
 76			lock.notifyAll();
 77		}
 78		return wreq;
 79	}
 80
 81	/**
 82	 *	Makes sure that there are enough threads to execute all requests
 83	 *	in parallel and enqueue the requests. It's not guaranteed that all
 84	 *	the requests will run in parallel, but the pool is guaranteed to
 85	 *	have enough running threads to do so.
 86	 *
 87	 *	<p>So if you enqueue 4 requests and there are 4 idle threads, each
 88	 *	request will run on a separate thread. But if one thread is running
 89	 *	a long request, it may happen that one of the new requests might
 90	 *	finish before that running job, and another one of the new requests
 91	 *	will run on that same thread.</p>
 92	 */
 93	public WorkRequest[] addRequests(Runnable[] reqs)
 94	{
 95		ensureCapacity(reqs.length);
 96		WorkRequest[] wreqs = toWorkRequest(reqs);
 97		synchronized (lock)
 98		{
 99			for (int i = 0; i < wreqs.length; i++)
100				requests.add(wreqs[i]);
101			lock.notifyAll();
102		}
103		return wreqs;
104	}
105
106	/**
107	 *	Immediately runs the given requests. If not enough worker threads
108	 *	are free to handle all the requests, new threads are created to
109	 *	be able to handle the new requests.
110	 *
111	 *	@since CC 0.9.1
112	 */
113	public WorkRequest[] runRequests(Runnable[] reqs)
114	{
115		WorkRequest[] wreqs = toWorkRequest(reqs);
116		ensureCapacity(wreqs.length);
117		synchronized (lock) {
118			int curr = 0;
119			for (Iterator i = threads.iterator();
120				 curr < wreqs.length && i.hasNext(); )
121			{
122				WorkerThread wt = (WorkerThread) i.next();
123				if (wt.isIdle()) {
124					wt.setWorkload(wreqs[curr++]);
125				}
126			}
127			for (int i = curr; i < wreqs.length; i++) {
128				WorkerThread t = new WorkerThread();
129				t.setWorkload(wreqs[i]);
130				t.start();
131				threads.add(t);
132			}
133			lock.notifyAll();
134		}
135		return wreqs;
136	}
137
138	/**
139	 *	Ensures that at least <code>size</code> threads are available to
140	 *	handle requests.
141	 */
142	public void ensureCapacity(int size) {
143		synchronized (lock)
144		{
145			if (threads == null)
146			{
147				threads = new LinkedList();
148			}
149
150			while (threads.size() < size)
151			{
152				Thread t = new WorkerThread();
153				t.start();
154				threads.add(t);
155			}
156		}
157	}
158
159	/** Asks all running threads to shutdown. */
160	public void shutdown()
161	{
162		synchronized (lock)
163		{
164			if (threads != null)
165			{
166				for (Iterator i = threads.iterator(); i.hasNext(); ) {
167					((WorkerThread)i.next()).requestShutdown();
168					i.remove();
169				}
170			}
171		}
172	}
173
174	private WorkRequest[] toWorkRequest(Runnable[] reqs) {
175		WorkRequest[] wreqs = new WorkRequest[reqs.length];
176		for (int i = 0; i < wreqs.length; i++)
177			wreqs[i] = new WorkRequest(reqs[i]);
178		return wreqs;
179	}
180
181	private static int THREAD_ID = 0;
182
183	private class WorkerThread extends Thread
184	{
185
186		private boolean run 		= true;
187		private int		idleCount	= 0;
188		private volatile WorkRequest work = null;
189
190		public WorkerThread() {
191			super(group, "CC::Worker #" + (++THREAD_ID));
192			setDaemon(true);
193		}
194
195		/** not synchronized. call while holding "lock". */
196		public void setWorkload(WorkRequest work) {
197			this.work = work;
198		}
199
200		/** not synchronized. call while holding "lock". */
201		public boolean isIdle() {
202			return (work == null);
203		}
204
205		public void run()
206		{
207			while (run)
208			{
209				idleCount = 0;
210				synchronized (lock)
211				{
212					while (run && work == null && idleCount < 10)
213					{
214						if (requests.size() > 0)
215						{
216							work = (WorkRequest) requests.remove(0);
217							break;
218						}
219
220						try {
221							lock.wait(10000);
222						} catch (InterruptedException ie) {
223							// ignore.
224							ie.printStackTrace();
225						}
226
227						idleCount++;
228					}
229				}
230				if (work != null)
231				{
232					Log.log(Log.NOTICE, this, "Executing request: " + work.getRunnable());
233					work.run();
234					work = null;
235				}
236				else if (idleCount >= 10)
237				{
238					// stop the thread if if has been inactive for a long
239					// time and there's more than 1 thread running
240					synchronized (lock)
241					{
242						run = !(threads.size() > 1);
243						if (!run)
244							threads.remove(this);
245					}
246				}
247			}
248		}
249
250		public void requestShutdown()
251		{
252			synchronized (lock) {
253				run = false;
254				lock.notifyAll();
255			}
256		}
257
258	}
259
260}