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 }