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