GNU Classpath (0.98) | |
Frames | No Frames |
1: /* 2: * Written by Doug Lea with assistance from members of JCP JSR-166 3: * Expert Group and released to the public domain, as explained at 4: * http://creativecommons.org/licenses/publicdomain 5: */ 6: 7: package java.util.concurrent; 8: import java.util.*; 9: import java.util.concurrent.atomic.AtomicInteger; 10: import java.security.AccessControlContext; 11: import java.security.AccessController; 12: import java.security.PrivilegedAction; 13: import java.security.PrivilegedExceptionAction; 14: import java.security.AccessControlException; 15: 16: /** 17: * Factory and utility methods for {@link Executor}, {@link 18: * ExecutorService}, {@link ScheduledExecutorService}, {@link 19: * ThreadFactory}, and {@link Callable} classes defined in this 20: * package. This class supports the following kinds of methods: 21: * 22: * <ul> 23: * <li> Methods that create and return an {@link ExecutorService} 24: * set up with commonly useful configuration settings. 25: * <li> Methods that create and return a {@link ScheduledExecutorService} 26: * set up with commonly useful configuration settings. 27: * <li> Methods that create and return a "wrapped" ExecutorService, that 28: * disables reconfiguration by making implementation-specific methods 29: * inaccessible. 30: * <li> Methods that create and return a {@link ThreadFactory} 31: * that sets newly created threads to a known state. 32: * <li> Methods that create and return a {@link Callable} 33: * out of other closure-like forms, so they can be used 34: * in execution methods requiring <tt>Callable</tt>. 35: * </ul> 36: * 37: * @since 1.5 38: * @author Doug Lea 39: */ 40: public class Executors { 41: 42: /** 43: * Creates a thread pool that reuses a fixed number of threads 44: * operating off a shared unbounded queue. At any point, at most 45: * <tt>nThreads</tt> threads will be active processing tasks. 46: * If additional tasks are submitted when all threads are active, 47: * they will wait in the queue until a thread is available. 48: * If any thread terminates due to a failure during execution 49: * prior to shutdown, a new one will take its place if needed to 50: * execute subsequent tasks. The threads in the pool will exist 51: * until it is explicitly {@link ExecutorService#shutdown shutdown}. 52: * 53: * @param nThreads the number of threads in the pool 54: * @return the newly created thread pool 55: * @throws IllegalArgumentException if <tt>nThreads <= 0</tt> 56: */ 57: public static ExecutorService newFixedThreadPool(int nThreads) { 58: return new ThreadPoolExecutor(nThreads, nThreads, 59: 0L, TimeUnit.MILLISECONDS, 60: new LinkedBlockingQueue<Runnable>()); 61: } 62: 63: /** 64: * Creates a thread pool that reuses a fixed number of threads 65: * operating off a shared unbounded queue, using the provided 66: * ThreadFactory to create new threads when needed. At any point, 67: * at most <tt>nThreads</tt> threads will be active processing 68: * tasks. If additional tasks are submitted when all threads are 69: * active, they will wait in the queue until a thread is 70: * available. If any thread terminates due to a failure during 71: * execution prior to shutdown, a new one will take its place if 72: * needed to execute subsequent tasks. The threads in the pool will 73: * exist until it is explicitly {@link ExecutorService#shutdown 74: * shutdown}. 75: * 76: * @param nThreads the number of threads in the pool 77: * @param threadFactory the factory to use when creating new threads 78: * @return the newly created thread pool 79: * @throws NullPointerException if threadFactory is null 80: * @throws IllegalArgumentException if <tt>nThreads <= 0</tt> 81: */ 82: public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { 83: return new ThreadPoolExecutor(nThreads, nThreads, 84: 0L, TimeUnit.MILLISECONDS, 85: new LinkedBlockingQueue<Runnable>(), 86: threadFactory); 87: } 88: 89: /** 90: * Creates an Executor that uses a single worker thread operating 91: * off an unbounded queue. (Note however that if this single 92: * thread terminates due to a failure during execution prior to 93: * shutdown, a new one will take its place if needed to execute 94: * subsequent tasks.) Tasks are guaranteed to execute 95: * sequentially, and no more than one task will be active at any 96: * given time. Unlike the otherwise equivalent 97: * <tt>newFixedThreadPool(1)</tt> the returned executor is 98: * guaranteed not to be reconfigurable to use additional threads. 99: * 100: * @return the newly created single-threaded Executor 101: */ 102: public static ExecutorService newSingleThreadExecutor() { 103: return new FinalizableDelegatedExecutorService 104: (new ThreadPoolExecutor(1, 1, 105: 0L, TimeUnit.MILLISECONDS, 106: new LinkedBlockingQueue<Runnable>())); 107: } 108: 109: /** 110: * Creates an Executor that uses a single worker thread operating 111: * off an unbounded queue, and uses the provided ThreadFactory to 112: * create a new thread when needed. Unlike the otherwise 113: * equivalent <tt>newFixedThreadPool(1, threadFactory)</tt> the 114: * returned executor is guaranteed not to be reconfigurable to use 115: * additional threads. 116: * 117: * @param threadFactory the factory to use when creating new 118: * threads 119: * 120: * @return the newly created single-threaded Executor 121: * @throws NullPointerException if threadFactory is null 122: */ 123: public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { 124: return new FinalizableDelegatedExecutorService 125: (new ThreadPoolExecutor(1, 1, 126: 0L, TimeUnit.MILLISECONDS, 127: new LinkedBlockingQueue<Runnable>(), 128: threadFactory)); 129: } 130: 131: /** 132: * Creates a thread pool that creates new threads as needed, but 133: * will reuse previously constructed threads when they are 134: * available. These pools will typically improve the performance 135: * of programs that execute many short-lived asynchronous tasks. 136: * Calls to <tt>execute</tt> will reuse previously constructed 137: * threads if available. If no existing thread is available, a new 138: * thread will be created and added to the pool. Threads that have 139: * not been used for sixty seconds are terminated and removed from 140: * the cache. Thus, a pool that remains idle for long enough will 141: * not consume any resources. Note that pools with similar 142: * properties but different details (for example, timeout parameters) 143: * may be created using {@link ThreadPoolExecutor} constructors. 144: * 145: * @return the newly created thread pool 146: */ 147: public static ExecutorService newCachedThreadPool() { 148: return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 149: 60L, TimeUnit.SECONDS, 150: new SynchronousQueue<Runnable>()); 151: } 152: 153: /** 154: * Creates a thread pool that creates new threads as needed, but 155: * will reuse previously constructed threads when they are 156: * available, and uses the provided 157: * ThreadFactory to create new threads when needed. 158: * @param threadFactory the factory to use when creating new threads 159: * @return the newly created thread pool 160: * @throws NullPointerException if threadFactory is null 161: */ 162: public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { 163: return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 164: 60L, TimeUnit.SECONDS, 165: new SynchronousQueue<Runnable>(), 166: threadFactory); 167: } 168: 169: /** 170: * Creates a single-threaded executor that can schedule commands 171: * to run after a given delay, or to execute periodically. 172: * (Note however that if this single 173: * thread terminates due to a failure during execution prior to 174: * shutdown, a new one will take its place if needed to execute 175: * subsequent tasks.) Tasks are guaranteed to execute 176: * sequentially, and no more than one task will be active at any 177: * given time. Unlike the otherwise equivalent 178: * <tt>newScheduledThreadPool(1)</tt> the returned executor is 179: * guaranteed not to be reconfigurable to use additional threads. 180: * @return the newly created scheduled executor 181: */ 182: public static ScheduledExecutorService newSingleThreadScheduledExecutor() { 183: return new DelegatedScheduledExecutorService 184: (new ScheduledThreadPoolExecutor(1)); 185: } 186: 187: /** 188: * Creates a single-threaded executor that can schedule commands 189: * to run after a given delay, or to execute periodically. (Note 190: * however that if this single thread terminates due to a failure 191: * during execution prior to shutdown, a new one will take its 192: * place if needed to execute subsequent tasks.) Tasks are 193: * guaranteed to execute sequentially, and no more than one task 194: * will be active at any given time. Unlike the otherwise 195: * equivalent <tt>newScheduledThreadPool(1, threadFactory)</tt> 196: * the returned executor is guaranteed not to be reconfigurable to 197: * use additional threads. 198: * @param threadFactory the factory to use when creating new 199: * threads 200: * @return a newly created scheduled executor 201: * @throws NullPointerException if threadFactory is null 202: */ 203: public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) { 204: return new DelegatedScheduledExecutorService 205: (new ScheduledThreadPoolExecutor(1, threadFactory)); 206: } 207: 208: /** 209: * Creates a thread pool that can schedule commands to run after a 210: * given delay, or to execute periodically. 211: * @param corePoolSize the number of threads to keep in the pool, 212: * even if they are idle. 213: * @return a newly created scheduled thread pool 214: * @throws IllegalArgumentException if <tt>corePoolSize < 0</tt> 215: */ 216: public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { 217: return new ScheduledThreadPoolExecutor(corePoolSize); 218: } 219: 220: /** 221: * Creates a thread pool that can schedule commands to run after a 222: * given delay, or to execute periodically. 223: * @param corePoolSize the number of threads to keep in the pool, 224: * even if they are idle. 225: * @param threadFactory the factory to use when the executor 226: * creates a new thread. 227: * @return a newly created scheduled thread pool 228: * @throws IllegalArgumentException if <tt>corePoolSize < 0</tt> 229: * @throws NullPointerException if threadFactory is null 230: */ 231: public static ScheduledExecutorService newScheduledThreadPool( 232: int corePoolSize, ThreadFactory threadFactory) { 233: return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); 234: } 235: 236: 237: /** 238: * Returns an object that delegates all defined {@link 239: * ExecutorService} methods to the given executor, but not any 240: * other methods that might otherwise be accessible using 241: * casts. This provides a way to safely "freeze" configuration and 242: * disallow tuning of a given concrete implementation. 243: * @param executor the underlying implementation 244: * @return an <tt>ExecutorService</tt> instance 245: * @throws NullPointerException if executor null 246: */ 247: public static ExecutorService unconfigurableExecutorService(ExecutorService executor) { 248: if (executor == null) 249: throw new NullPointerException(); 250: return new DelegatedExecutorService(executor); 251: } 252: 253: /** 254: * Returns an object that delegates all defined {@link 255: * ScheduledExecutorService} methods to the given executor, but 256: * not any other methods that might otherwise be accessible using 257: * casts. This provides a way to safely "freeze" configuration and 258: * disallow tuning of a given concrete implementation. 259: * @param executor the underlying implementation 260: * @return a <tt>ScheduledExecutorService</tt> instance 261: * @throws NullPointerException if executor null 262: */ 263: public static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) { 264: if (executor == null) 265: throw new NullPointerException(); 266: return new DelegatedScheduledExecutorService(executor); 267: } 268: 269: /** 270: * Returns a default thread factory used to create new threads. 271: * This factory creates all new threads used by an Executor in the 272: * same {@link ThreadGroup}. If there is a {@link 273: * java.lang.SecurityManager}, it uses the group of {@link 274: * System#getSecurityManager}, else the group of the thread 275: * invoking this <tt>defaultThreadFactory</tt> method. Each new 276: * thread is created as a non-daemon thread with priority set to 277: * the smaller of <tt>Thread.NORM_PRIORITY</tt> and the maximum 278: * priority permitted in the thread group. New threads have names 279: * accessible via {@link Thread#getName} of 280: * <em>pool-N-thread-M</em>, where <em>N</em> is the sequence 281: * number of this factory, and <em>M</em> is the sequence number 282: * of the thread created by this factory. 283: * @return a thread factory 284: */ 285: public static ThreadFactory defaultThreadFactory() { 286: return new DefaultThreadFactory(); 287: } 288: 289: /** 290: * Returns a thread factory used to create new threads that 291: * have the same permissions as the current thread. 292: * This factory creates threads with the same settings as {@link 293: * Executors#defaultThreadFactory}, additionally setting the 294: * AccessControlContext and contextClassLoader of new threads to 295: * be the same as the thread invoking this 296: * <tt>privilegedThreadFactory</tt> method. A new 297: * <tt>privilegedThreadFactory</tt> can be created within an 298: * {@link AccessController#doPrivileged} action setting the 299: * current thread's access control context to create threads with 300: * the selected permission settings holding within that action. 301: * 302: * <p> Note that while tasks running within such threads will have 303: * the same access control and class loader settings as the 304: * current thread, they need not have the same {@link 305: * java.lang.ThreadLocal} or {@link 306: * java.lang.InheritableThreadLocal} values. If necessary, 307: * particular values of thread locals can be set or reset before 308: * any task runs in {@link ThreadPoolExecutor} subclasses using 309: * {@link ThreadPoolExecutor#beforeExecute}. Also, if it is 310: * necessary to initialize worker threads to have the same 311: * InheritableThreadLocal settings as some other designated 312: * thread, you can create a custom ThreadFactory in which that 313: * thread waits for and services requests to create others that 314: * will inherit its values. 315: * 316: * @return a thread factory 317: * @throws AccessControlException if the current access control 318: * context does not have permission to both get and set context 319: * class loader. 320: */ 321: public static ThreadFactory privilegedThreadFactory() { 322: return new PrivilegedThreadFactory(); 323: } 324: 325: /** 326: * Returns a {@link Callable} object that, when 327: * called, runs the given task and returns the given result. This 328: * can be useful when applying methods requiring a 329: * <tt>Callable</tt> to an otherwise resultless action. 330: * @param task the task to run 331: * @param result the result to return 332: * @return a callable object 333: * @throws NullPointerException if task null 334: */ 335: public static <T> Callable<T> callable(Runnable task, T result) { 336: if (task == null) 337: throw new NullPointerException(); 338: return new RunnableAdapter<T>(task, result); 339: } 340: 341: /** 342: * Returns a {@link Callable} object that, when 343: * called, runs the given task and returns <tt>null</tt>. 344: * @param task the task to run 345: * @return a callable object 346: * @throws NullPointerException if task null 347: */ 348: public static Callable<Object> callable(Runnable task) { 349: if (task == null) 350: throw new NullPointerException(); 351: return new RunnableAdapter<Object>(task, null); 352: } 353: 354: /** 355: * Returns a {@link Callable} object that, when 356: * called, runs the given privileged action and returns its result. 357: * @param action the privileged action to run 358: * @return a callable object 359: * @throws NullPointerException if action null 360: */ 361: public static Callable<Object> callable(final PrivilegedAction<?> action) { 362: if (action == null) 363: throw new NullPointerException(); 364: return new Callable<Object>() { 365: public Object call() { return action.run(); }}; 366: } 367: 368: /** 369: * Returns a {@link Callable} object that, when 370: * called, runs the given privileged exception action and returns 371: * its result. 372: * @param action the privileged exception action to run 373: * @return a callable object 374: * @throws NullPointerException if action null 375: */ 376: public static Callable<Object> callable(final PrivilegedExceptionAction<?> action) { 377: if (action == null) 378: throw new NullPointerException(); 379: return new Callable<Object>() { 380: public Object call() throws Exception { return action.run(); }}; 381: } 382: 383: /** 384: * Returns a {@link Callable} object that will, when 385: * called, execute the given <tt>callable</tt> under the current 386: * access control context. This method should normally be 387: * invoked within an {@link AccessController#doPrivileged} action 388: * to create callables that will, if possible, execute under the 389: * selected permission settings holding within that action; or if 390: * not possible, throw an associated {@link 391: * AccessControlException}. 392: * @param callable the underlying task 393: * @return a callable object 394: * @throws NullPointerException if callable null 395: * 396: */ 397: public static <T> Callable<T> privilegedCallable(Callable<T> callable) { 398: if (callable == null) 399: throw new NullPointerException(); 400: return new PrivilegedCallable<T>(callable); 401: } 402: 403: /** 404: * Returns a {@link Callable} object that will, when 405: * called, execute the given <tt>callable</tt> under the current 406: * access control context, with the current context class loader 407: * as the context class loader. This method should normally be 408: * invoked within an {@link AccessController#doPrivileged} action 409: * to create callables that will, if possible, execute under the 410: * selected permission settings holding within that action; or if 411: * not possible, throw an associated {@link 412: * AccessControlException}. 413: * @param callable the underlying task 414: * 415: * @return a callable object 416: * @throws NullPointerException if callable null 417: * @throws AccessControlException if the current access control 418: * context does not have permission to both set and get context 419: * class loader. 420: */ 421: public static <T> Callable<T> privilegedCallableUsingCurrentClassLoader(Callable<T> callable) { 422: if (callable == null) 423: throw new NullPointerException(); 424: return new PrivilegedCallableUsingCurrentClassLoader<T>(callable); 425: } 426: 427: // Non-public classes supporting the public methods 428: 429: /** 430: * A callable that runs given task and returns given result 431: */ 432: static final class RunnableAdapter<T> implements Callable<T> { 433: final Runnable task; 434: final T result; 435: RunnableAdapter(Runnable task, T result) { 436: this.task = task; 437: this.result = result; 438: } 439: public T call() { 440: task.run(); 441: return result; 442: } 443: } 444: 445: /** 446: * A callable that runs under established access control settings 447: */ 448: static final class PrivilegedCallable<T> implements Callable<T> { 449: private final AccessControlContext acc; 450: private final Callable<T> task; 451: private T result; 452: private Exception exception; 453: PrivilegedCallable(Callable<T> task) { 454: this.task = task; 455: this.acc = AccessController.getContext(); 456: } 457: 458: public T call() throws Exception { 459: AccessController.doPrivileged(new PrivilegedAction<T>() { 460: public T run() { 461: try { 462: result = task.call(); 463: } catch (Exception ex) { 464: exception = ex; 465: } 466: return null; 467: } 468: }, acc); 469: if (exception != null) 470: throw exception; 471: else 472: return result; 473: } 474: } 475: 476: /** 477: * A callable that runs under established access control settings and 478: * current ClassLoader 479: */ 480: static final class PrivilegedCallableUsingCurrentClassLoader<T> implements Callable<T> { 481: private final ClassLoader ccl; 482: private final AccessControlContext acc; 483: private final Callable<T> task; 484: private T result; 485: private Exception exception; 486: PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) { 487: this.task = task; 488: this.ccl = Thread.currentThread().getContextClassLoader(); 489: this.acc = AccessController.getContext(); 490: acc.checkPermission(new RuntimePermission("getContextClassLoader")); 491: acc.checkPermission(new RuntimePermission("setContextClassLoader")); 492: } 493: 494: public T call() throws Exception { 495: AccessController.doPrivileged(new PrivilegedAction<T>() { 496: public T run() { 497: ClassLoader savedcl = null; 498: Thread t = Thread.currentThread(); 499: try { 500: ClassLoader cl = t.getContextClassLoader(); 501: if (ccl != cl) { 502: t.setContextClassLoader(ccl); 503: savedcl = cl; 504: } 505: result = task.call(); 506: } catch (Exception ex) { 507: exception = ex; 508: } finally { 509: if (savedcl != null) 510: t.setContextClassLoader(savedcl); 511: } 512: return null; 513: } 514: }, acc); 515: if (exception != null) 516: throw exception; 517: else 518: return result; 519: } 520: } 521: 522: /** 523: * The default thread factory 524: */ 525: static class DefaultThreadFactory implements ThreadFactory { 526: static final AtomicInteger poolNumber = new AtomicInteger(1); 527: final ThreadGroup group; 528: final AtomicInteger threadNumber = new AtomicInteger(1); 529: final String namePrefix; 530: 531: DefaultThreadFactory() { 532: SecurityManager s = System.getSecurityManager(); 533: group = (s != null)? s.getThreadGroup() : 534: Thread.currentThread().getThreadGroup(); 535: namePrefix = "pool-" + 536: poolNumber.getAndIncrement() + 537: "-thread-"; 538: } 539: 540: public Thread newThread(Runnable r) { 541: Thread t = new Thread(group, r, 542: namePrefix + threadNumber.getAndIncrement(), 543: 0); 544: if (t.isDaemon()) 545: t.setDaemon(false); 546: if (t.getPriority() != Thread.NORM_PRIORITY) 547: t.setPriority(Thread.NORM_PRIORITY); 548: return t; 549: } 550: } 551: 552: /** 553: * Thread factory capturing access control and class loader 554: */ 555: static class PrivilegedThreadFactory extends DefaultThreadFactory { 556: private final ClassLoader ccl; 557: private final AccessControlContext acc; 558: 559: PrivilegedThreadFactory() { 560: super(); 561: this.ccl = Thread.currentThread().getContextClassLoader(); 562: this.acc = AccessController.getContext(); 563: acc.checkPermission(new RuntimePermission("setContextClassLoader")); 564: } 565: 566: public Thread newThread(final Runnable r) { 567: return super.newThread(new Runnable() { 568: public void run() { 569: AccessController.doPrivileged(new PrivilegedAction<Object>() { 570: public Object run() { 571: Thread.currentThread().setContextClassLoader(ccl); 572: r.run(); 573: return null; 574: } 575: }, acc); 576: } 577: }); 578: } 579: 580: } 581: 582: /** 583: * A wrapper class that exposes only the ExecutorService methods 584: * of an ExecutorService implementation. 585: */ 586: static class DelegatedExecutorService extends AbstractExecutorService { 587: private final ExecutorService e; 588: DelegatedExecutorService(ExecutorService executor) { e = executor; } 589: public void execute(Runnable command) { e.execute(command); } 590: public void shutdown() { e.shutdown(); } 591: public List<Runnable> shutdownNow() { return e.shutdownNow(); } 592: public boolean isShutdown() { return e.isShutdown(); } 593: public boolean isTerminated() { return e.isTerminated(); } 594: public boolean awaitTermination(long timeout, TimeUnit unit) 595: throws InterruptedException { 596: return e.awaitTermination(timeout, unit); 597: } 598: public Future<?> submit(Runnable task) { 599: return e.submit(task); 600: } 601: public <T> Future<T> submit(Callable<T> task) { 602: return e.submit(task); 603: } 604: public <T> Future<T> submit(Runnable task, T result) { 605: return e.submit(task, result); 606: } 607: public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) 608: throws InterruptedException { 609: return e.invokeAll(tasks); 610: } 611: public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, 612: long timeout, TimeUnit unit) 613: throws InterruptedException { 614: return e.invokeAll(tasks, timeout, unit); 615: } 616: public <T> T invokeAny(Collection<? extends Callable<T>> tasks) 617: throws InterruptedException, ExecutionException { 618: return e.invokeAny(tasks); 619: } 620: public <T> T invokeAny(Collection<? extends Callable<T>> tasks, 621: long timeout, TimeUnit unit) 622: throws InterruptedException, ExecutionException, TimeoutException { 623: return e.invokeAny(tasks, timeout, unit); 624: } 625: } 626: 627: static class FinalizableDelegatedExecutorService 628: extends DelegatedExecutorService { 629: FinalizableDelegatedExecutorService(ExecutorService executor) { 630: super(executor); 631: } 632: protected void finalize() { 633: super.shutdown(); 634: } 635: } 636: 637: /** 638: * A wrapper class that exposes only the ScheduledExecutorService 639: * methods of a ScheduledExecutorService implementation. 640: */ 641: static class DelegatedScheduledExecutorService 642: extends DelegatedExecutorService 643: implements ScheduledExecutorService { 644: private final ScheduledExecutorService e; 645: DelegatedScheduledExecutorService(ScheduledExecutorService executor) { 646: super(executor); 647: e = executor; 648: } 649: public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { 650: return e.schedule(command, delay, unit); 651: } 652: public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { 653: return e.schedule(callable, delay, unit); 654: } 655: public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { 656: return e.scheduleAtFixedRate(command, initialDelay, period, unit); 657: } 658: public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { 659: return e.scheduleWithFixedDelay(command, initialDelay, delay, unit); 660: } 661: } 662: 663: 664: /** Cannot instantiate. */ 665: private Executors() {} 666: }
GNU Classpath (0.98) |