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