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