001/* 002 * $Id: ThreadPool.java 5495 2016-04-24 21:27:50Z kredel $ 003 */ 004 005// package edu.unima.ky.parallel; 006package edu.jas.util; 007 008 009import java.util.LinkedList; 010 011import org.apache.log4j.Logger; 012 013import edu.jas.kern.PreemptingException; 014 015 016/** 017 * Thread pool using stack / list workpile. 018 * @author Akitoshi Yoshida 019 * @author Heinz Kredel 020 */ 021 022public class ThreadPool { 023 024 025 /** 026 * Default number of threads to use. 027 */ 028 static final int DEFAULT_SIZE = 3; 029 030 031 /** 032 * Number of threads to use. 033 */ 034 final int size; 035 036 037 /** 038 * Array of workers. 039 */ 040 protected PoolThread[] workers; 041 042 043 /** 044 * Number of idle workers. 045 */ 046 protected int idleworkers = 0; 047 048 049 /** 050 * Shutdown request. 051 */ 052 protected volatile boolean shutdown = false; 053 054 055 /** 056 * Work queue / stack. 057 */ 058 // should be expressed using strategy pattern 059 // List or Collection is not appropriate 060 // LIFO strategy for recursion 061 protected LinkedList<Runnable> jobstack; // FIFO strategy for GB 062 063 064 protected StrategyEnumeration strategy = StrategyEnumeration.LIFO; 065 066 067 private static final Logger logger = Logger.getLogger(ThreadPool.class); 068 069 070 private static final boolean debug = logger.isDebugEnabled(); 071 072 073 /** 074 * Constructs a new ThreadPool with strategy StrategyEnumeration.FIFO and 075 * size DEFAULT_SIZE. 076 */ 077 public ThreadPool() { 078 this(StrategyEnumeration.FIFO, DEFAULT_SIZE); 079 } 080 081 082 /** 083 * Constructs a new ThreadPool with size DEFAULT_SIZE. 084 * @param strategy for job processing. 085 */ 086 public ThreadPool(StrategyEnumeration strategy) { 087 this(strategy, DEFAULT_SIZE); 088 } 089 090 091 /** 092 * Constructs a new ThreadPool with strategy StrategyEnumeration.FIFO. 093 * @param size of the pool. 094 */ 095 public ThreadPool(int size) { 096 this(StrategyEnumeration.FIFO, size); 097 } 098 099 100 /** 101 * Constructs a new ThreadPool. 102 * @param strategy for job processing. 103 * @param size of the pool. 104 */ 105 public ThreadPool(StrategyEnumeration strategy, int size) { 106 this.size = size; 107 this.strategy = strategy; 108 jobstack = new LinkedList<Runnable>(); // ok for all strategies ? 109 workers = new PoolThread[0]; 110 } 111 112 113 /** 114 * thread initialization and start. 115 */ 116 public void init() { 117 if (workers == null || workers.length == 0) { 118 workers = new PoolThread[size]; 119 for (int i = 0; i < workers.length; i++) { 120 workers[i] = new PoolThread(this); 121 workers[i].start(); 122 } 123 logger.info("size = " + size + ", strategy = " + strategy); 124 } 125 if (debug) { 126 Thread.dumpStack(); 127 } 128 } 129 130 131 /** 132 * toString. 133 */ 134 @Override 135 public String toString() { 136 return "ThreadPool( size=" + getNumber() + ", idle=" + idleworkers + ", " + getStrategy() + ", jobs=" 137 + jobstack.size() + ")"; 138 } 139 140 141 /** 142 * number of worker threads. 143 */ 144 public int getNumber() { 145 return size; 146 //if (workers == null || workers.length < size) { 147 // init(); // start threads 148 //} 149 //return workers.length; // not null 150 } 151 152 153 /** 154 * get used strategy. 155 */ 156 public StrategyEnumeration getStrategy() { 157 return strategy; 158 } 159 160 161 /** 162 * Terminates the threads. 163 */ 164 public void terminate() { 165 while (hasJobs()) { 166 try { 167 Thread.sleep(100); 168 //logger.info("waiting for termination in " + this); 169 } catch (InterruptedException e) { 170 Thread.currentThread().interrupt(); 171 } 172 } 173 if (workers == null) { 174 return; 175 } 176 for (int i = 0; i < workers.length; i++) { 177 if (workers[i] == null) { 178 continue; 179 } 180 try { 181 while (workers[i].isAlive()) { 182 workers[i].interrupt(); 183 workers[i].join(100); 184 } 185 } catch (InterruptedException e) { 186 Thread.currentThread().interrupt(); 187 } 188 } 189 } 190 191 192 /** 193 * Cancels the threads. 194 */ 195 public int cancel() { 196 shutdown = true; 197 int s = jobstack.size(); 198 if (hasJobs()) { 199 synchronized (this) { 200 logger.info("jobs canceled: " + jobstack); 201 jobstack.clear(); 202 notifyAll(); // for getJob 203 } 204 } 205 //int re = 0; 206 if (workers == null) { 207 return s; 208 } 209 for (int i = 0; i < workers.length; i++) { 210 if (workers[i] == null) { 211 continue; 212 } 213 try { 214 while (workers[i].isAlive()) { 215 synchronized (this) { 216 shutdown = true; 217 notifyAll(); // for getJob 218 workers[i].interrupt(); 219 } 220 //re++; 221 //if ( re > 3 * workers.length ) { 222 // logger.info("give up on: " + workers[i]); 223 // break; // give up 224 //} 225 workers[i].join(100); 226 } 227 } catch (InterruptedException e) { 228 Thread.currentThread().interrupt(); 229 } 230 } 231 return s; 232 } 233 234 235 /** 236 * adds a job to the workpile. 237 * @param job 238 */ 239 public synchronized void addJob(Runnable job) { 240 if (workers == null || workers.length < size) { 241 init(); // start threads 242 } 243 jobstack.addLast(job); 244 logger.debug("adding job"); 245 if (idleworkers > 0) { 246 logger.debug("notifying a jobless worker"); 247 notifyAll(); 248 } 249 } 250 251 252 /** 253 * get a job for processing. 254 */ 255 protected synchronized Runnable getJob() throws InterruptedException { 256 while (jobstack.isEmpty()) { 257 idleworkers++; 258 logger.debug("waiting"); 259 wait(1000); 260 idleworkers--; 261 if (shutdown) { 262 throw new InterruptedException("shutdown in getJob"); 263 } 264 } 265 // is expressed using strategy enumeration 266 if (strategy == StrategyEnumeration.LIFO) { 267 return jobstack.removeLast(); // LIFO 268 } 269 return jobstack.removeFirst(); // FIFO 270 } 271 272 273 /** 274 * check if there are jobs for processing. 275 */ 276 public boolean hasJobs() { 277 if (jobstack.size() > 0) { 278 return true; 279 } 280 for (int i = 0; i < workers.length; i++) { 281 if (workers[i] == null) { 282 continue; 283 } 284 if (workers[i].isWorking) { 285 return true; 286 } 287 } 288 return false; 289 } 290 291 292 /** 293 * check if there are more than n jobs for processing. 294 * @param n Integer 295 * @return true, if there are possibly more than n jobs. 296 */ 297 public boolean hasJobs(int n) { 298 int j = jobstack.size(); 299 if (j > 0 && (j + workers.length > n)) { 300 return true; 301 } 302 // if j > 0 no worker should be idle 303 // ( ( j > 0 && ( j+workers.length > n ) ) || ( j > n ) 304 int x = 0; 305 for (int i = 0; i < workers.length; i++) { 306 if (workers[i] == null) { 307 continue; 308 } 309 if (workers[i].isWorking) { 310 x++; 311 } 312 } 313 if ((j + x) > n) { 314 return true; 315 } 316 return false; 317 } 318 319} 320 321 322/** 323 * Implements one Thread of the pool. 324 */ 325class PoolThread extends Thread { 326 327 328 ThreadPool pool; 329 330 331 private static final Logger logger = Logger.getLogger(PoolThread.class); 332 333 334 private static final boolean debug = logger.isDebugEnabled(); 335 336 337 volatile boolean isWorking = false; 338 339 340 /** 341 * @param pool ThreadPool. 342 */ 343 public PoolThread(ThreadPool pool) { 344 this.pool = pool; 345 } 346 347 348 /** 349 * Run the thread. 350 */ 351 @Override 352 public void run() { 353 logger.info("ready"); 354 Runnable job; 355 int done = 0; 356 long time = 0; 357 long t; 358 boolean running = true; 359 while (running) { 360 try { 361 logger.debug("looking for a job"); 362 job = pool.getJob(); 363 if (job == null) { 364 break; 365 } 366 if (debug) { 367 logger.info("working"); 368 } 369 t = System.currentTimeMillis(); 370 isWorking = true; 371 job.run(); 372 isWorking = false; 373 time += System.currentTimeMillis() - t; 374 done++; 375 if (debug) { 376 logger.info("done"); 377 } 378 if (Thread.currentThread().isInterrupted()) { 379 running = false; 380 isWorking = false; 381 //throw new RuntimeException("interrupt in while(running) loop"); 382 } 383 } catch (InterruptedException e) { 384 Thread.currentThread().interrupt(); 385 running = false; 386 isWorking = false; 387 } catch (PreemptingException e) { 388 logger.debug("catched " + e); 389 //e.printStackTrace(); 390 } catch (RuntimeException e) { 391 logger.warn("catched " + e); 392 e.printStackTrace(); 393 } 394 } 395 isWorking = false; 396 logger.info("terminated, done " + done + " jobs in " + time + " milliseconds"); 397 } 398 399}