ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/Executors.java
Revision: 1.74
Committed: Thu Nov 24 02:35:13 2011 UTC (12 years, 6 months ago) by jsr166
Branch: MAIN
Changes since 1.73: +0 -2 lines
Log Message:
whitespace

File Contents

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