001/* 002 * $Id: DistThreadPool.java 5788 2018-01-04 21:07:10Z kredel $ 003 */ 004 005package edu.jas.util; 006 007 008import java.io.FileNotFoundException; 009import java.io.IOException; 010import java.util.LinkedList; 011 012import org.apache.log4j.Logger; 013 014 015/** 016 * Distributed thread pool. Using stack / list work-pile and Executable Channels 017 * and Servers. 018 * @author Heinz Kredel 019 */ 020 021public class DistThreadPool /*extends ThreadPool*/{ 022 023 024 /** 025 * machine file to use. 026 */ 027 private final String mfile; 028 029 030 /** 031 * default machine file for test. 032 */ 033 private final static String DEFAULT_MFILE = ExecutableChannels.DEFAULT_MFILE; 034 035 036 /** 037 * Number of threads to use. 038 */ 039 protected final int threads; 040 041 042 /** 043 * Default number of threads to use. 044 */ 045 static final int DEFAULT_SIZE = 3; 046 047 048 /** 049 * Channels to remote executable servers. 050 */ 051 final ExecutableChannels ec; 052 053 054 /** 055 * Array of workers. 056 */ 057 protected DistPoolThread[] workers; 058 059 060 /** 061 * Number of idle workers. 062 */ 063 protected int idleworkers = 0; 064 065 066 /** 067 * Work queue / stack. 068 */ 069 // should be expressed using strategy pattern 070 // List or Collection is not appropriate 071 // LIFO strategy for recursion 072 protected LinkedList<Runnable> jobstack; // FIFO strategy for GB 073 074 075 protected StrategyEnumeration strategy = StrategyEnumeration.LIFO; 076 077 078 private static final Logger logger = Logger.getLogger(DistThreadPool.class); 079 080 081 private static final boolean debug = true; //logger.isDebugEnabled(); 082 083 084 /** 085 * Constructs a new DistThreadPool with strategy StrategyEnumeration.FIFO 086 * and size DEFAULT_SIZE. 087 */ 088 public DistThreadPool() { 089 this(StrategyEnumeration.FIFO, DEFAULT_SIZE, null); 090 } 091 092 093 /** 094 * Constructs a new DistThreadPool with size DEFAULT_SIZE. 095 * @param strategy for job processing. 096 */ 097 public DistThreadPool(StrategyEnumeration strategy) { 098 this(strategy, DEFAULT_SIZE, null); 099 } 100 101 102 /** 103 * Constructs a new DistThreadPool with strategy StrategyEnumeration.FIFO. 104 * @param size of the pool. 105 */ 106 public DistThreadPool(int size) { 107 this(StrategyEnumeration.FIFO, size, null); 108 } 109 110 111 /** 112 * Constructs a new DistThreadPool with strategy StrategyEnumeration.FIFO. 113 * @param size of the pool. 114 * @param mfile machine file. 115 */ 116 public DistThreadPool(int size, String mfile) { 117 this(StrategyEnumeration.FIFO, size, mfile); 118 } 119 120 121 /** 122 * Constructs a new DistThreadPool. 123 * @param strategy for job processing. 124 * @param size of the pool. 125 * @param mfile machine file. 126 */ 127 public DistThreadPool(StrategyEnumeration strategy, int size, String mfile) { 128 this.strategy = strategy; 129 if (size < 0) { 130 this.threads = 0; 131 } else { 132 this.threads = size; 133 } 134 if (mfile == null || mfile.length() == 0) { 135 this.mfile = DEFAULT_MFILE; 136 } else { 137 this.mfile = mfile; 138 } 139 jobstack = new LinkedList<Runnable>(); // ok for all strategies ? 140 try { 141 ec = new ExecutableChannels(this.mfile); 142 } catch (FileNotFoundException e) { 143 e.printStackTrace(); 144 throw new IllegalArgumentException("DistThreadPool " + e); 145 } 146 if (debug) { 147 logger.info("ec = " + ec); 148 } 149 try { 150 ec.open(threads); 151 } catch (IOException e) { 152 e.printStackTrace(); 153 throw new IllegalArgumentException("DistThreadPool " + e); 154 } 155 if (debug) { 156 logger.info("ec = " + ec); 157 } 158 workers = new DistPoolThread[0]; 159 } 160 161 162 /** 163 * String representation. 164 */ 165 @Override 166 public String toString() { 167 StringBuffer s = new StringBuffer("DistThreadPool("); 168 s.append("threads="+threads); 169 s.append(", strategy="+strategy); 170 s.append(", exchan="+ec); 171 s.append(", workers="+workers.length); 172 s.append(")"); 173 return s.toString(); 174 } 175 176 177 /** 178 * thread initialization and start. 179 */ 180 public void init() { 181 if (workers == null || workers.length == 0) { 182 workers = new DistPoolThread[threads]; 183 for (int i = 0; i < workers.length; i++) { 184 workers[i] = new DistPoolThread(this, ec, i); 185 workers[i].start(); 186 } 187 logger.info("init: " + this.toString()); 188 } 189 } 190 191 192 /** 193 * number of worker threads. 194 */ 195 public int getNumber() { 196 if (workers == null || workers.length < threads) { 197 init(); // start threads 198 } 199 return workers.length; // not null 200 } 201 202 203 /** 204 * get used strategy. 205 */ 206 public StrategyEnumeration getStrategy() { 207 return strategy; 208 } 209 210 211 /** 212 * the used executable channel. 213 */ 214 public ExecutableChannels getEC() { 215 return ec; // not null 216 } 217 218 219 /** 220 * Terminates the threads. 221 * @param shutDown true, if shut-down of the remote executable servers is 222 * requested, false, if remote executable servers stay alive. 223 */ 224 public void terminate(boolean shutDown) { 225 if (shutDown) { 226 logger.info("shutdown = " + this); 227 ShutdownRequest sdr = new ShutdownRequest(); 228 for (int i = 0; i < workers.length; i++) { 229 addJob(sdr); 230 } 231 try { 232 Thread.sleep(20); 233 } catch (InterruptedException e) { 234 Thread.currentThread().interrupt(); 235 } 236 logger.info("remaining jobs = " + jobstack.size()); 237 try { 238 for (int i = 0; i < workers.length; i++) { 239 while (workers[i].isAlive()) { 240 workers[i].interrupt(); 241 workers[i].join(100); 242 } 243 } 244 } catch (InterruptedException e) { 245 Thread.currentThread().interrupt(); 246 } 247 ec.close(); 248 } else { 249 terminate(); 250 } 251 logger.info("terminated = " + this); 252 } 253 254 255 /** 256 * Terminates the threads. 257 */ 258 public void terminate() { 259 logger.info("terminate = " + this); 260 while (hasJobs()) { 261 try { 262 Thread.sleep(100); 263 } catch (InterruptedException e) { 264 Thread.currentThread().interrupt(); 265 } 266 } 267 for (int i = 0; i < workers.length; i++) { 268 try { 269 while (workers[i].isAlive()) { 270 workers[i].interrupt(); 271 workers[i].join(100); 272 } 273 } catch (InterruptedException e) { 274 Thread.currentThread().interrupt(); 275 } 276 } 277 ec.close(); 278 logger.info("terminated = " + this); 279 } 280 281 282 /** 283 * adds a job to the workpile. 284 * @param job 285 */ 286 public synchronized void addJob(Runnable job) { 287 if (workers == null || workers.length < threads) { 288 init(); // start threads 289 } 290 jobstack.addLast(job); 291 logger.debug("adding job"); 292 if (idleworkers > 0) { 293 logger.debug("notifying a jobless worker"); 294 notifyAll(); // findbugs 295 } 296 } 297 298 299 /** 300 * get a job for processing. 301 */ 302 protected synchronized Runnable getJob() throws InterruptedException { 303 while (jobstack.isEmpty()) { 304 idleworkers++; 305 logger.debug("waiting"); 306 wait(); 307 idleworkers--; 308 } 309 // is expressed using strategy enumeration 310 if (strategy == StrategyEnumeration.LIFO) { 311 return jobstack.removeLast(); // LIFO 312 } 313 return jobstack.removeFirst(); // FIFO 314 } 315 316 317 /** 318 * check if there are jobs for processing. 319 */ 320 public boolean hasJobs() { 321 if (jobstack.size() > 0) { 322 return true; 323 } 324 for (int i = 0; i < workers.length; i++) { 325 if (workers[i].working) { 326 return true; 327 } 328 } 329 return false; 330 } 331 332 333 /** 334 * check if there are more than n jobs for processing. 335 * @param n Integer 336 * @return true, if there are possibly more than n jobs. 337 */ 338 public boolean hasJobs(int n) { 339 int j = jobstack.size(); 340 if (j > 0 && (j + workers.length > n)) { 341 return true; 342 // if j > 0 no worker should be idle 343 // ( ( j > 0 && ( j+workers.length > n ) ) || ( j > n ) 344 } 345 int x = 0; 346 for (int i = 0; i < workers.length; i++) { 347 if (workers[i].working) { 348 x++; 349 } 350 } 351 if ((j + x) > n) { 352 return true; 353 } 354 return false; 355 } 356 357} 358 359 360/** 361 * Implements a shutdown task. 362 */ 363class ShutdownRequest implements Runnable { 364 365 366 /** 367 * Run the thread. 368 */ 369 public void run() { 370 System.out.println("running ShutdownRequest"); 371 } 372 373 374 /** 375 * toString. 376 * @see java.lang.Object#toString() 377 */ 378 @Override 379 public String toString() { 380 return "ShutdownRequest"; 381 } 382 383} 384 385 386/** 387 * Implements one local part of the distributed thread. 388 */ 389class DistPoolThread extends Thread { 390 391 392 final DistThreadPool pool; 393 394 395 final ExecutableChannels ec; 396 397 398 final int myId; 399 400 401 private static final Logger logger = Logger.getLogger(DistPoolThread.class); 402 403 404 private static final boolean debug = logger.isDebugEnabled(); 405 406 407 boolean working = false; 408 409 410 /** 411 * @param pool DistThreadPool. 412 */ 413 public DistPoolThread(DistThreadPool pool, ExecutableChannels ec, int i) { 414 this.pool = pool; 415 this.ec = ec; 416 myId = i; 417 } 418 419 420 /** 421 * Run the thread. 422 */ 423 @Override 424 public void run() { 425 logger.info("ready, myId = " + myId); 426 Runnable job; 427 int done = 0; 428 long time = 0; 429 long t; 430 boolean running = true; 431 while (running) { 432 try { 433 logger.debug("looking for a job"); 434 job = pool.getJob(); 435 working = true; 436 if (debug) { 437 logger.info("working " + myId + " on " + job); 438 } 439 t = System.currentTimeMillis(); 440 // send and wait, like rmi 441 try { 442 if (job instanceof ShutdownRequest) { 443 ec.send(myId, ExecutableServer.STOP); 444 } else { 445 ec.send(myId, job); 446 } 447 if (debug) { 448 logger.info("send " + myId + " at " + ec + " send job " + job); 449 } 450 } catch (IOException e) { 451 e.printStackTrace(); 452 logger.info("error send " + myId + " at " + ec + " e = " + e); 453 working = false; 454 } 455 // remote: job.run(); 456 Object o = null; 457 try { 458 if (working) { 459 logger.info("waiting " + myId + " on " + job); 460 o = ec.receive(myId); 461 if (debug) { 462 logger.info("receive " + myId + " at " + ec + " send job " + job + " received " + o); 463 } 464 } 465 } catch (IOException e) { 466 logger.info("receive exception " + myId + " send job " + job + ", " + e); 467 //e.printStackTrace(); 468 running = false; 469 } catch (ClassNotFoundException e) { 470 logger.info("receive exception " + myId + " send job " + job + ", " + e); 471 //e.printStackTrace(); 472 running = false; 473 } finally { 474 if (debug) { 475 logger.info("receive finally " + myId + " at " + ec + " send job " + job + " received " 476 + o + " running " + running); 477 } 478 } 479 working = false; 480 time += System.currentTimeMillis() - t; 481 done++; 482 if (debug) { 483 logger.info("done " + myId + " with " + o); 484 } 485 } catch (InterruptedException e) { 486 running = false; 487 Thread.currentThread().interrupt(); 488 } 489 } 490 logger.info("terminated " + myId + " , done " + done + " jobs in " + time + " milliseconds"); 491 } 492 493}