001 /* 002 * $Id: SwingWorker.java,v 1.4 2006/04/20 00:14:17 gfx Exp $ 003 * 004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021 022 package org.jdesktop.swingx.util; 023 024 import java.beans.PropertyChangeListener; 025 import java.beans.PropertyChangeSupport; 026 027 import java.util.concurrent.*; 028 import java.util.concurrent.locks.*; 029 030 import javax.swing.SwingUtilities; 031 032 /** 033 * An abstract class to perform lengthy GUI-interacting tasks in a 034 * dedicated thread. 035 * 036 * <p> 037 * When writing a multi-threaded application using Swing, there are 038 * two constraints to keep in mind: 039 * (refer to 040 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html"> 041 * How to Use Threads 042 * </a> for more details): 043 * <ul> 044 * <li> Time-consuming tasks should not be run on the <i>Event 045 * Dispatch Thread</i>. Otherwise the application becomes unresponsive. 046 * </li> 047 * <li> Swing components should be accessed on the <i>Event 048 * Dispatch Thread</i> only. 049 * </li> 050 * </ul> 051 * 052 * <p> 053 * 054 * <p> 055 * These constraints mean that a GUI application with time intensive 056 * computing needs at least two threads: 1) a thread to perform the lengthy 057 * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related 058 * activities. This involves inter-thread communication which can be 059 * tricky to implement. 060 * 061 * <p> 062 * {@code SwingWorker} is designed for situations where you need to have a long 063 * running task run in a background thread and provide updates to the UI 064 * either when done, or while processing. 065 * Subclasses of {@code SwingWorker} must implement 066 * the {@see #doInBackground} method to perform the background computation. 067 * 068 * 069 * <p> 070 * <b>Workflow</b> 071 * <p> 072 * There are three threads involved in the life cycle of a 073 * {@code SwingWorker} : 074 * <ul> 075 * <li> 076 * <p> 077 * <i>Current</i> thread: The {@link #execute} method is 078 * called on this thread. It schedules {@code SwingWorker} for the execution on a 079 * <i>worker</i> 080 * thread and returns immediately. One can wait for the {@code SwingWorker} to 081 * complete using the {@link #get get} methods. 082 * <li> 083 * <p> 084 * <i>Worker</i> thread: The {@link #doInBackground} 085 * method is called on this thread. 086 * This is where all background activities should happen. To notify 087 * {@code PropertyChangeListeners} about bound properties changes use the 088 * {@link #firePropertyChange firePropertyChange} and 089 * {@link #getPropertyChangeSupport} methods. By default there are two bound 090 * properties available: {@code state} and {@code progress}. 091 * <li> 092 * <p> 093 * <i>Event Dispatch Thread</i>: All Swing related activities occur 094 * on this thread. {@code SwingWorker} invokes the 095 * {@link #process process} and {@link #done} methods and notifies 096 * any {@code PropertyChangeListeners} on this thread. 097 * </ul> 098 * 099 * <p> 100 * Often, the <i>Current</i> thread is the <i>Event Dispatch 101 * Thread</i>. 102 * 103 * 104 * <p> 105 * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread, 106 * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the 107 * {@code state} property change to {@code StateValue.STARTED}. After the 108 * {@code doInBackground} method is finished the {@code done} method is 109 * executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners} 110 * about the {@code state} property change to {@code StateValue.DONE}. 111 * 112 * <p> 113 * {@code SwingWorker} is only designed to be executed once. Executing a 114 * {@code SwingWorker} more than once will not result in invoking the 115 * {@code doInBackground} method twice. 116 * 117 * <p> 118 * <b>Sample Usage</b> 119 * <p> 120 * The following example illustrates the simplest use case. Some 121 * processing is done in the background and when done you update a Swing 122 * component. 123 * 124 * <p> 125 * Say we want to find the "Meaning of Life" and display the result in 126 * a {@code JLabel}. 127 * 128 * <pre> 129 * final JLabel label; 130 * class MeaningOfLifeFinder extends SwingWorker<String, Object> { 131 * {@code @Override} 132 * public String doInBackground() { 133 * return findTheMeaningOfLife(); 134 * } 135 * 136 * {@code @Override} 137 * protected void done() { 138 * try { 139 * label.setText(get()); 140 * } catch (Exception ignore) { 141 * } 142 * } 143 * } 144 * 145 * (new MeaningOfLifeFinder()).execute(); 146 * </pre> 147 * 148 * <p> 149 * The next example is useful in situations where you wish to process data 150 * as it is ready on the <i>Event Dispatch Thread</i>. 151 * 152 * <p> 153 * Now we want to find the first N prime numbers and display the results in a 154 * {@code JTextArea}. While this is computing, we want to update our 155 * progress in a {@code JProgressBar}. Finally, we also want to print 156 * the prime numbers to {@code System.out}. 157 * <pre> 158 * class PrimeNumbersTask extends 159 * SwingWorker<List<Integer>, Integer> { 160 * PrimeNumbersTask(JTextArea textArea, int numbersToFind) { 161 * //initialize 162 * } 163 * 164 * {@code @Override} 165 * public List<Integer> doInBackground() { 166 * while (! enough && ! isCancelled()) { 167 * number = nextPrimeNumber(); 168 * publish(number); 169 * setProgress(100 * numbers.size() / numbersToFind); 170 * } 171 * } 172 * return numbers; 173 * } 174 * 175 * {@code @Override} 176 * protected void process(Integer... chunks) { 177 * for (int number : chunks) { 178 * textArea.append(number + "\n"); 179 * } 180 * } 181 * } 182 * 183 * JTextArea textArea = new JTextArea(); 184 * final JProgressBar progressBar = new JProgressBar(0, 100); 185 * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N); 186 * task.addPropertyChangeListener( 187 * new PropertyChangeListener() { 188 * public void propertyChange(PropertyChangeEvent evt) { 189 * if ("progress".equals(evt.getPropertyName())) { 190 * progressBar.setValue((Integer)evt.getNewValue()); 191 * } 192 * } 193 * }); 194 * 195 * task.execute(); 196 * System.out.println(task.get()); //prints all prime numbers we have got 197 * </pre> 198 * 199 * <p> 200 * Because {@code SwingWorker} implements {@code Runnable}, a 201 * {@code SwingWorker} can be submitted to an 202 * {@link java.util.concurrent.Executor} for execution. 203 * 204 * @author Igor Kushnirskiy 205 * @version $Revision: 1.4 $ $Date: 2006/04/20 00:14:17 $ 206 * 207 * @param <T> the result type returned by this {@code SwingWorker's} 208 * {@code doInBackground} and {@code get} methods 209 * @param <V> the type used for carrying out intermediate results by this 210 * {@code SwingWorker's} {@code publish} and {@code process} methods 211 * 212 */ 213 public abstract class SwingWorker<T, V> implements Future<T>, Runnable { 214 /** 215 * number of worker threads. 216 */ 217 private static final int MAX_WORKER_THREADS = 10; 218 219 /** 220 * current progress. 221 */ 222 private volatile int progress; 223 224 /** 225 * current state. 226 */ 227 private volatile StateValue state; 228 229 /** 230 * everything is run inside this FutureTask. Also it is used as 231 * a delegatee for the Future API. 232 */ 233 private final FutureTask<T> future; 234 235 /** 236 * all propertyChangeSupport goes through this. 237 */ 238 private final SwingPropertyChangeSupport propertyChangeSupport; 239 240 /** 241 * handler for {@code process} mehtod. 242 */ 243 private AccumulativeRunnable<V> doProcess; 244 245 /** 246 * handler for progress property change notifications. 247 */ 248 private AccumulativeRunnable<Integer> doNotifyProgressChange; 249 250 private static ExecutorService executorService = null; 251 252 /** 253 * Values for the {@code state} bound property. 254 */ 255 public enum StateValue { 256 /** 257 * Initial {@code SwingWorker} state. 258 */ 259 PENDING, 260 /** 261 * {@code SwingWorker} is {@code STARTED} 262 * before invoking {@code doInBackground}. 263 */ 264 STARTED, 265 266 /** 267 * {@code SwingWorker} is {@code DONE} 268 * after {@code doInBackground} method 269 * is finished. 270 */ 271 DONE 272 } 273 274 /** 275 * Constructs this {@code SwingWorker}. 276 */ 277 public SwingWorker() { 278 Callable<T> callable = 279 new Callable<T>() { 280 public T call() throws Exception { 281 setState(StateValue.STARTED); 282 return doInBackground(); 283 } 284 }; 285 286 future = new FutureTask<T>(callable) { 287 @Override 288 protected void done() { 289 doneEDT(); 290 setState(StateValue.DONE); 291 } 292 }; 293 294 state = StateValue.PENDING; 295 propertyChangeSupport = new SwingPropertyChangeSupport(this, true); 296 297 doProcess = null; 298 doNotifyProgressChange = null; 299 } 300 301 /** 302 * Computes a result, or throws an exception if unable to do so. 303 * 304 * <p> 305 * Note that this method is executed only once. 306 * 307 * <p> 308 * Note: this method is executed in a background thread. 309 * 310 * 311 * @return the computed result 312 * @throws Exception if unable to compute a result 313 * 314 */ 315 protected abstract T doInBackground() throws Exception ; 316 317 /** 318 * Sets this {@code Future} to the result of computation unless 319 * it has been cancelled. 320 */ 321 public final void run() { 322 future.run(); 323 } 324 325 /** 326 * Sends data chunks to the {@link #process} method. This method is to be 327 * used from inside the {@code doInBackground} method to deliver 328 * intermediate results 329 * for processing on the <i>Event Dispatch Thread</i> inside the 330 * {@code process} method. 331 * 332 * <p> 333 * Because the {@code process} method is invoked asynchronously on 334 * the <i>Event Dispatch Thread</i> 335 * multiple invocations to the {@code publish} method 336 * might occur before the {@code process} method is executed. For 337 * performance purposes all these invocations are coalesced into one 338 * invocation with concatenated arguments. 339 * 340 * <p> 341 * For example: 342 * 343 * <pre> 344 * publish("1"); 345 * publish("2", "3"); 346 * publish("4", "5", "6"); 347 * </pre> 348 * 349 * might result in: 350 * 351 * <pre> 352 * process("1", "2", "3", "4", "5", "6") 353 * </pre> 354 * 355 * <p> 356 * <b>Sample Usage</b>. This code snippet loads some tabular data and 357 * updates {@code DefaultTableModel} with it. Note that it safe to mutate 358 * the tableModel from inside the {@code process} method because it is 359 * invoked on the <i>Event Dispatch Thread</i>. 360 * 361 * <pre> 362 * class TableSwingWorker extends 363 * SwingWorker<DefaultTableModel, Object[]> { 364 * private final DefaultTableModel tableModel; 365 * 366 * public TableSwingWorker(DefaultTableModel tableModel) { 367 * this.tableModel = tableModel; 368 * } 369 * 370 * {@code @Override} 371 * protected DefaultTableModel doInBackground() throws Exception { 372 * for (Object[] row = loadData(); 373 * ! isCancelled() && row != null; 374 * row = loadData()) { 375 * publish((Object[]) row); 376 * } 377 * return tableModel; 378 * } 379 * 380 * {@code @Override} 381 * protected void process(Object[]... chunks) { 382 * for (Object[] row : chunks) { 383 * tableModel.addRow(row); 384 * } 385 * } 386 * } 387 * </pre> 388 * 389 * @param chunks intermediate results to process 390 * 391 * @see #process 392 * 393 */ 394 protected final void publish(V... chunks) { 395 synchronized (this) { 396 if (doProcess == null) { 397 doProcess = new AccumulativeRunnable<V>() { 398 @Override 399 public void run(V... args) { 400 process(args); 401 } 402 }; 403 } 404 } 405 doProcess.add(chunks); 406 } 407 408 /** 409 * Receives data chunks from the {@code publish} method asynchronously on the 410 * <i>Event Dispatch Thread</i>. 411 * 412 * <p> 413 * Please refer to the {@link #publish} method for more details. 414 * 415 * @param chunks intermediate results to process 416 * 417 * @see #publish 418 * 419 */ 420 protected void process(V... chunks) { 421 } 422 423 /** 424 * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground} 425 * method is finished. The default 426 * implementation does nothing. Subclasses may override this method to 427 * perform completion actions on the <i>Event Dispatch Thread</i>. Note 428 * that you can query status inside the implementation of this method to 429 * determine the result of this task or whether this task has been cancelled. 430 * 431 * @see #doInBackground 432 * @see #isCancelled() 433 * @see #get 434 */ 435 protected void done() { 436 } 437 438 /** 439 * Sets the {@code progress} bound property. 440 * The value should be from 0 to 100. 441 * 442 * <p> 443 * Because {@code PropertyChangeListener}s are notified asynchronously on 444 * the <i>Event Dispatch Thread</i> multiple invocations to the 445 * {@code setProgress} method might occur before any 446 * {@code PropertyChangeListeners} are invoked. For performance purposes 447 * all these invocations are coalesced into one invocation with the last 448 * invocation argument only. 449 * 450 * <p> 451 * For example, the following invokations: 452 * 453 * <pre> 454 * setProgress(1); 455 * setProgress(2); 456 * setProgress(3); 457 * </pre> 458 * 459 * might result in a single {@code PropertyChangeListener} notification with 460 * the value {@code 3}. 461 * 462 * @param progress the progress value to set 463 * @throws IllegalArgumentException is value not from 0 to 100 464 */ 465 protected final void setProgress(int progress) { 466 if (progress < 0 || progress > 100) { 467 throw new IllegalArgumentException("the value should be from 0 to 100"); 468 } 469 int oldProgress = this.progress; 470 this.progress = progress; 471 synchronized (this) { 472 if (doNotifyProgressChange == null) { 473 doNotifyProgressChange = 474 new AccumulativeRunnable<Integer>() { 475 @Override 476 public void run(Integer... args) { 477 firePropertyChange("progress", 478 args[0], 479 args[args.length - 1]); 480 } 481 }; 482 } 483 } 484 doNotifyProgressChange.add(oldProgress, progress); 485 } 486 487 /** 488 * Returns the {@code progress} bound property. 489 * 490 * @return the progress bound property. 491 */ 492 public final int getProgress() { 493 return progress; 494 } 495 496 /** 497 * Schedules this {@code SwingWorker} for execution on a <i>worker</i> 498 * thread. There are a number of <i>worker</i> threads available. In the 499 * event all <i>worker</i> threads are busy handling other 500 * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting 501 * queue. 502 * 503 * <p> 504 * Note: 505 * {@code SwingWorker} is only designed to be executed once. Executing a 506 * {@code SwingWorker} more than once will not result in invoking the 507 * {@code doInBackground} method twice. 508 */ 509 public final void execute() { 510 getWorkersExecutorService().execute(this); 511 } 512 513 // Future methods START 514 /** 515 * {@inheritDoc} 516 */ 517 public final boolean cancel(boolean mayInterruptIfRunning) { 518 return future.cancel(mayInterruptIfRunning); 519 } 520 521 /** 522 * {@inheritDoc} 523 */ 524 public final boolean isCancelled() { 525 return future.isCancelled(); 526 } 527 528 /** 529 * {@inheritDoc} 530 */ 531 public final boolean isDone() { 532 return future.isDone(); 533 } 534 535 /** 536 * {@inheritDoc} 537 * <p> 538 * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks 539 * <i>all</i> events, including repaints, from being processed until this 540 * {@code SwingWorker} is complete. 541 * 542 * <p> 543 * When you want the {@code SwingWorker} to block on the <i>Event 544 * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>. 545 * 546 * <p> 547 * For example: 548 * 549 * <pre> 550 * class SwingWorkerCompletionWaiter extends PropertyChangeListener { 551 * private JDialog dialog; 552 * 553 * public SwingWorkerCompletionWaiter(JDialog dialog) { 554 * this.dialog = dialog; 555 * } 556 * 557 * public void propertyChange(PropertyChangeEvent event) { 558 * if ("state".equals(event.getPropertyName()) 559 * && SwingWorker.StateValue.DONE == event.getNewValue()) { 560 * dialog.setVisible(false); 561 * dialog.dispose(); 562 * } 563 * } 564 * } 565 * JDialog dialog = new JDialog(owner, true); 566 * swingWorker.addPropertyChangeListener( 567 * new SwingWorkerCompletionWaiter(dialog)); 568 * swingWorker.execute(); 569 * //the dialog will be visible until the SwingWorker is done 570 * dialog.setVisible(true); 571 * </pre> 572 */ 573 public final T get() throws InterruptedException, ExecutionException { 574 return future.get(); 575 } 576 577 /** 578 * {@inheritDoc} 579 * <p> 580 * Please refer to {@link #get} for more details. 581 */ 582 public final T get(long timeout, TimeUnit unit) throws InterruptedException, 583 ExecutionException, TimeoutException { 584 return future.get(timeout, unit); 585 } 586 587 // Future methods END 588 589 // PropertyChangeSupports methods START 590 /** 591 * Adds a {@code PropertyChangeListener} to the listener list. The listener 592 * is registered for all properties. The same listener object may be added 593 * more than once, and will be called as many times as it is added. If 594 * {@code listener} is {@code null}, no exception is thrown and no action is taken. 595 * 596 * <p> 597 * Note: This is merely a convenience wrapper. All work is delegated to 598 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. 599 * 600 * @param listener the {@code PropertyChangeListener} to be added 601 */ 602 public final void addPropertyChangeListener(PropertyChangeListener listener) { 603 getPropertyChangeSupport().addPropertyChangeListener(listener); 604 } 605 606 /** 607 * Removes a {@code PropertyChangeListener} from the listener list. This 608 * removes a {@code PropertyChangeListener} that was registered for all 609 * properties. If {@code listener} was added more than once to the same 610 * event source, it will be notified one less time after being removed. If 611 * {@code listener} is {@code null}, or was never added, no exception is 612 * thrown and no action is taken. 613 * 614 * <p> 615 * Note: This is merely a convenience wrapper. All work is delegated to 616 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. 617 * 618 * @param listener the {@code PropertyChangeListener} to be removed 619 */ 620 public final void removePropertyChangeListener(PropertyChangeListener listener) { 621 getPropertyChangeSupport().removePropertyChangeListener(listener); 622 } 623 624 /** 625 * Reports a bound property update to any registered listeners. No event is 626 * fired if {@code old} and {@code new} are equal and non-null. 627 * 628 * <p> 629 * This {@code SwingWorker} will be the source for 630 * any generated events. 631 * 632 * <p> 633 * When called off the <i>Event Dispatch Thread</i> 634 * {@code PropertyChangeListeners} are notified asynchronously on 635 * the <i>Event Dispatch Thread</i>. 636 * <p> 637 * Note: This is merely a convenience wrapper. All work is delegated to 638 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. 639 * 640 * 641 * @param propertyName the programmatic name of the property that was 642 * changed 643 * @param oldValue the old value of the property 644 * @param newValue the new value of the property 645 */ 646 public final void firePropertyChange(String propertyName, Object oldValue, 647 Object newValue) { 648 getPropertyChangeSupport().firePropertyChange(propertyName, 649 oldValue, newValue); 650 } 651 652 /** 653 * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}. 654 * This method is used when flexible access to bound properties support is 655 * needed. 656 * <p> 657 * This {@code SwingWorker} will be the source for 658 * any generated events. 659 * 660 * <p> 661 * Note: The returned {@code PropertyChangeSupport} notifies any 662 * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch 663 * Thread</i> in the event that {@code firePropertyChange} or 664 * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch 665 * Thread</i>. 666 * 667 * @return {@code PropertyChangeSupport} for this {@code SwingWorker} 668 */ 669 public final PropertyChangeSupport getPropertyChangeSupport() { 670 return propertyChangeSupport; 671 } 672 673 // PropertyChangeSupports methods END 674 675 /** 676 * Returns the {@code SwingWorker} state bound property. 677 * 678 * @return the current state 679 */ 680 public final StateValue getState() { 681 /* 682 * DONE is a special case 683 * to keep getState and isDone is sync 684 */ 685 if (isDone()) { 686 return StateValue.DONE; 687 } else { 688 return state; 689 } 690 } 691 692 /** 693 * Sets this {@code SwingWorker} state bound property. 694 * @param state the state to set 695 */ 696 private void setState(StateValue state) { 697 StateValue old = this.state; 698 this.state = state; 699 firePropertyChange("state", old, state); 700 } 701 702 /** 703 * Invokes {@code done} on the EDT. 704 */ 705 private void doneEDT() { 706 Runnable doDone = 707 new Runnable() { 708 public void run() { 709 done(); 710 } 711 }; 712 if (SwingUtilities.isEventDispatchThread()) { 713 doDone.run(); 714 } else { 715 SwingUtilities.invokeLater(doDone); 716 } 717 } 718 719 720 /** 721 * returns workersExecutorService. 722 * 723 * returns the service stored in the appContext or creates it if 724 * necessary. If the last one it triggers autoShutdown thread to 725 * get started. 726 * 727 * @return ExecutorService for the {@code SwingWorkers} 728 */ 729 private static synchronized ExecutorService getWorkersExecutorService() { 730 if (executorService == null) { 731 //this creates non-daemon threads. 732 ThreadFactory threadFactory = 733 new ThreadFactory() { 734 final ThreadFactory defaultFactory = 735 Executors.defaultThreadFactory(); 736 public Thread newThread(final Runnable r) { 737 Thread thread = 738 defaultFactory.newThread(r); 739 thread.setName("SwingWorker-" 740 + thread.getName()); 741 thread.setPriority(Thread.MIN_PRIORITY); 742 return thread; 743 } 744 }; 745 746 /* 747 * We want a to have no more than MAX_WORKER_THREADS 748 * running threads. 749 * 750 * We want a worker thread to wait no longer than 1 second 751 * for new tasks before terminating. 752 */ 753 executorService = new ThreadPoolExecutor(0, MAX_WORKER_THREADS, 754 1L, TimeUnit.SECONDS, 755 new LinkedBlockingQueue<Runnable>(), 756 threadFactory) { 757 758 private final ReentrantLock pauseLock = new ReentrantLock(); 759 private final Condition unpaused = pauseLock.newCondition(); 760 private boolean isPaused = false; 761 private final ReentrantLock executeLock = new ReentrantLock(); 762 763 @Override 764 public void execute(Runnable command) { 765 /* 766 * ThreadPoolExecutor first tries to run task 767 * in a corePool. If all threads are busy it 768 * tries to add task to the waiting queue. If it 769 * fails it run task in maximumPool. 770 * 771 * We want corePool to be 0 and 772 * maximumPool to be MAX_WORKER_THREADS 773 * We need to change the order of the execution. 774 * First try corePool then try maximumPool 775 * pool and only then store to the waiting 776 * queue. We can not do that because we would 777 * need access to the private methods. 778 * 779 * Instead we enlarge corePool to 780 * MAX_WORKER_THREADS before the execution and 781 * shrink it back to 0 after. 782 * It does pretty much what we need. 783 * 784 * While we changing the corePoolSize we need 785 * to stop running worker threads from accepting new 786 * tasks. 787 */ 788 789 //we need atomicity for the execute method. 790 executeLock.lock(); 791 try { 792 793 pauseLock.lock(); 794 try { 795 isPaused = true; 796 } finally { 797 pauseLock.unlock(); 798 } 799 800 setCorePoolSize(MAX_WORKER_THREADS); 801 super.execute(command); 802 setCorePoolSize(0); 803 804 pauseLock.lock(); 805 try { 806 isPaused = false; 807 unpaused.signalAll(); 808 } finally { 809 pauseLock.unlock(); 810 } 811 } finally { 812 executeLock.unlock(); 813 } 814 } 815 @Override 816 protected void afterExecute(Runnable r, Throwable t) { 817 super.afterExecute(r, t); 818 pauseLock.lock(); 819 try { 820 while(isPaused) { 821 unpaused.await(); 822 } 823 } catch(InterruptedException ignore) { 824 825 } finally { 826 pauseLock.unlock(); 827 } 828 } 829 }; 830 } 831 return executorService; 832 } 833 }