1. /*
  2. * @(#)ThreadPool.java 1.5 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.corba.se.internal.orbutil;
  8. import java.util.LinkedList;
  9. import java.util.NoSuchElementException;
  10. /**
  11. * Generic way to create a pool of threads. Users define classes that implement
  12. * the Work interface for different kinds of tasks.
  13. *
  14. * Possible future improvements:
  15. *
  16. * Management of the pooled threads (further inactivity work, pool size, etc)
  17. *
  18. * Priority of work
  19. *
  20. * Debug messages
  21. */
  22. public final class ThreadPool
  23. {
  24. private static int threadCounter = 0; // serial counter useful for debugging
  25. private WorkQueue workToDo = new WorkQueue();
  26. private int availableWorkers = 0;
  27. private int inactivityTimeout;
  28. public ThreadPool() {
  29. inactivityTimeout = ORBConstants.DEFAULT_INACTIVITY_TIMEOUT;
  30. }
  31. public ThreadPool(int inactivityTimeout) {
  32. this.inactivityTimeout = inactivityTimeout;
  33. }
  34. private static synchronized int getUniqueThreadId() {
  35. return ThreadPool.threadCounter++;
  36. }
  37. /*
  38. * Note that the methods are not synchronized since they are
  39. * only used in the requestWork and addWork methods which
  40. * are already synchronized.
  41. */
  42. private static final class WorkQueue
  43. {
  44. private LinkedList workToDo = new LinkedList();
  45. final void enqueue(Work work)
  46. {
  47. workToDo.add(work);
  48. }
  49. final Work dequeue() throws NoSuchElementException
  50. {
  51. return (Work)workToDo.removeFirst();
  52. }
  53. final boolean isEmpty()
  54. {
  55. return workToDo.isEmpty();
  56. }
  57. }
  58. /**
  59. * Generic thread in the pool. Simply requests and
  60. * processes Work instances. The while(true) loop
  61. * could be replaced by something that checks for a
  62. * shutdown value set by the ThreadPool.
  63. */
  64. private class PooledThread extends Thread
  65. {
  66. private Work currentWork;
  67. private int threadId = 0; // unique id for the thread
  68. PooledThread() {
  69. this.threadId = ThreadPool.getUniqueThreadId();
  70. }
  71. public void run()
  72. {
  73. while (true) {
  74. try {
  75. // Set the name to Idle for debugging
  76. setName("Idle");
  77. // Get some work to do
  78. currentWork = requestWork();
  79. // Set the name to the specific type
  80. // of work for debugging.
  81. StringBuffer buff = new StringBuffer();
  82. buff.append(currentWork.getName());
  83. buff.append('[');
  84. buff.append(this.threadId);
  85. buff.append(']');
  86. setName(buff.toString());
  87. // Do the work
  88. currentWork.process();
  89. } catch (TimeoutException e) {
  90. // This thread timed out waiting for something to do,
  91. // so it can exit.
  92. return;
  93. } catch (Exception e) {
  94. // Ignore any exceptions that currentWork.process
  95. // accidently lets through, but let Errors pass.
  96. // Add debugging output? REVISIT
  97. // Note that InterruptedExceptions are
  98. // caught here. Thus, threads can be forced out of
  99. // requestWork and so they have to reacquire the lock.
  100. // Other options include ignoring or
  101. // letting this thread die.
  102. }
  103. }
  104. }
  105. }
  106. /**
  107. * Called by a PooledThread to get the next appropriate
  108. * Work.
  109. */
  110. private synchronized Work requestWork()
  111. throws TimeoutException, InterruptedException
  112. {
  113. availableWorkers++;
  114. try {
  115. if (workToDo.isEmpty()) {
  116. this.wait(inactivityTimeout);
  117. // Did we time out?
  118. if (workToDo.isEmpty())
  119. throw new TimeoutException();
  120. }
  121. } finally {
  122. availableWorkers--;
  123. }
  124. return workToDo.dequeue();
  125. }
  126. /**
  127. * Used by classes which access the ThreadPool to add new
  128. * tasks.
  129. */
  130. public synchronized void addWork(Work work)
  131. {
  132. workToDo.enqueue(work);
  133. if (availableWorkers == 0) {
  134. PooledThread thread = new PooledThread();
  135. // The thread must be set to a daemon thread so the
  136. // VM can exit if the only threads left are PooledThreads
  137. // or other daemons. We don't want to rely on the
  138. // calling thread always being a daemon.
  139. // Catch exceptions since setDaemon can cause a
  140. // security exception to be thrown under netscape
  141. // in the Applet mode
  142. try {
  143. thread.setDaemon(true);
  144. } catch (Exception e) {}
  145. thread.start();
  146. } else
  147. this.notify();
  148. }
  149. private static class TimeoutException extends Exception {}
  150. }