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