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