1 /* 2 * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing; 27 28 import java.awt.event.ActionEvent; 29 import java.awt.event.ActionListener; 30 import java.beans.PropertyChangeEvent; 31 import java.beans.PropertyChangeListener; 32 import java.beans.PropertyChangeSupport; 33 import java.lang.ref.WeakReference; 34 import java.security.AccessController; 35 import java.security.PrivilegedAction; 36 import java.util.List; 37 import java.util.concurrent.Callable; 38 import java.util.concurrent.CancellationException; 39 import java.util.concurrent.ExecutionException; 40 import java.util.concurrent.ExecutorService; 41 import java.util.concurrent.Executors; 42 import java.util.concurrent.FutureTask; 43 import java.util.concurrent.LinkedBlockingQueue; 44 import java.util.concurrent.RunnableFuture; 45 import java.util.concurrent.ThreadFactory; 46 import java.util.concurrent.ThreadPoolExecutor; 47 import java.util.concurrent.TimeUnit; 48 import java.util.concurrent.TimeoutException; 49 50 import sun.awt.AppContext; 51 import sun.swing.AccumulativeRunnable; 52 53 /** 54 * An abstract class to perform lengthy GUI-interaction tasks in a 55 * background thread. Several background threads can be used to execute such 56 * tasks. However, the exact strategy of choosing a thread for any particular 57 * {@code SwingWorker} is unspecified and should not be relied on. 58 * <p> 59 * When writing a multi-threaded application using Swing, there are 60 * two constraints to keep in mind: 61 * (refer to 62 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html"> 63 * Concurrency in Swing 64 * </a> for more details): 65 * <ul> 66 * <li> Time-consuming tasks should not be run on the <i>Event 67 * Dispatch Thread</i>. Otherwise the application becomes unresponsive. 68 * </li> 69 * <li> Swing components should be accessed on the <i>Event 70 * Dispatch Thread</i> only. 71 * </li> 72 * </ul> 73 * 74 * 75 * <p> 76 * These constraints mean that a GUI application with time intensive 77 * computing needs at least two threads: 1) a thread to perform the lengthy 78 * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related 79 * activities. This involves inter-thread communication which can be 80 * tricky to implement. 81 * 82 * <p> 83 * {@code SwingWorker} is designed for situations where you need to have a long 84 * running task run in a background thread and provide updates to the UI 85 * either when done, or while processing. 86 * Subclasses of {@code SwingWorker} must implement 87 * the {@link #doInBackground} method to perform the background computation. 88 * 89 * 90 * <p> 91 * <b>Workflow</b> 92 * <p> 93 * There are three threads involved in the life cycle of a 94 * {@code SwingWorker} : 95 * <ul> 96 * <li> 97 * <p> 98 * <i>Current</i> thread: The {@link #execute} method is 99 * called on this thread. It schedules {@code SwingWorker} for the execution on a 100 * <i>worker</i> 101 * thread and returns immediately. One can wait for the {@code SwingWorker} to 102 * complete using the {@link #get get} methods. 103 * <li> 104 * <p> 105 * <i>Worker</i> thread: The {@link #doInBackground} 106 * method is called on this thread. 107 * This is where all background activities should happen. To notify 108 * {@code PropertyChangeListeners} about bound properties changes use the 109 * {@link #firePropertyChange firePropertyChange} and 110 * {@link #getPropertyChangeSupport} methods. By default there are two bound 111 * properties available: {@code state} and {@code progress}. 112 * <li> 113 * <p> 114 * <i>Event Dispatch Thread</i>: All Swing related activities occur 115 * on this thread. {@code SwingWorker} invokes the 116 * {@link #process process} and {@link #done} methods and notifies 117 * any {@code PropertyChangeListeners} on this thread. 118 * </ul> 119 * 120 * <p> 121 * Often, the <i>Current</i> thread is the <i>Event Dispatch 122 * Thread</i>. 123 * 124 * 125 * <p> 126 * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread, 127 * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the 128 * {@code state} property change to {@code StateValue.STARTED}. After the 129 * {@code doInBackground} method is finished the {@code done} method is 130 * executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners} 131 * about the {@code state} property change to {@code StateValue.DONE}. 132 * 133 * <p> 134 * {@code SwingWorker} is only designed to be executed once. Executing a 135 * {@code SwingWorker} more than once will not result in invoking the 136 * {@code doInBackground} method twice. 137 * 138 * <p> 139 * <b>Sample Usage</b> 140 * <p> 141 * The following example illustrates the simplest use case. Some 142 * processing is done in the background and when done you update a Swing 143 * component. 144 * 145 * <p> 146 * Say we want to find the "Meaning of Life" and display the result in 147 * a {@code JLabel}. 148 * 149 * <pre> 150 * final JLabel label; 151 * class MeaningOfLifeFinder extends SwingWorker<String, Object> { 152 * {@code @Override} 153 * public String doInBackground() { 154 * return findTheMeaningOfLife(); 155 * } 156 * 157 * {@code @Override} 158 * protected void done() { 159 * try { 160 * label.setText(get()); 161 * } catch (Exception ignore) { 162 * } 163 * } 164 * } 165 * 166 * (new MeaningOfLifeFinder()).execute(); 167 * </pre> 168 * 169 * <p> 170 * The next example is useful in situations where you wish to process data 171 * as it is ready on the <i>Event Dispatch Thread</i>. 172 * 173 * <p> 174 * Now we want to find the first N prime numbers and display the results in a 175 * {@code JTextArea}. While this is computing, we want to update our 176 * progress in a {@code JProgressBar}. Finally, we also want to print 177 * the prime numbers to {@code System.out}. 178 * <pre> 179 * class PrimeNumbersTask extends 180 * SwingWorker<List<Integer>, Integer> { 181 * PrimeNumbersTask(JTextArea textArea, int numbersToFind) { 182 * //initialize 183 * } 184 * 185 * {@code @Override} 186 * public List<Integer> doInBackground() { 187 * while (! enough && ! isCancelled()) { 188 * number = nextPrimeNumber(); 189 * publish(number); 190 * setProgress(100 * numbers.size() / numbersToFind); 191 * } 192 * } 193 * return numbers; 194 * } 195 * 196 * {@code @Override} 197 * protected void process(List<Integer> chunks) { 198 * for (int number : chunks) { 199 * textArea.append(number + "\n"); 200 * } 201 * } 202 * } 203 * 204 * JTextArea textArea = new JTextArea(); 205 * final JProgressBar progressBar = new JProgressBar(0, 100); 206 * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N); 207 * task.addPropertyChangeListener( 208 * new PropertyChangeListener() { 209 * public void propertyChange(PropertyChangeEvent evt) { 210 * if ("progress".equals(evt.getPropertyName())) { 211 * progressBar.setValue((Integer)evt.getNewValue()); 212 * } 213 * } 214 * }); 215 * 216 * task.execute(); 217 * System.out.println(task.get()); //prints all prime numbers we have got 218 * </pre> 219 * 220 * <p> 221 * Because {@code SwingWorker} implements {@code Runnable}, a 222 * {@code SwingWorker} can be submitted to an 223 * {@link java.util.concurrent.Executor} for execution. 224 * 225 * @author Igor Kushnirskiy 226 * 227 * @param <T> the result type returned by this {@code SwingWorker's} 228 * {@code doInBackground} and {@code get} methods 229 * @param <V> the type used for carrying out intermediate results by this 230 * {@code SwingWorker's} {@code publish} and {@code process} methods 231 * 232 * @since 1.6 233 */ 234 public abstract class SwingWorker<T, V> implements RunnableFuture<T> { 235 /** 236 * number of worker threads. 237 */ 238 private static final int MAX_WORKER_THREADS = 10; 239 240 /** 241 * current progress. 242 */ 243 private volatile int progress; 244 245 /** 246 * current state. 247 */ 248 private volatile StateValue state; 249 250 /** 251 * everything is run inside this FutureTask. Also it is used as 252 * a delegatee for the Future API. 253 */ 254 private final FutureTask<T> future; 255 256 /** 257 * all propertyChangeSupport goes through this. 258 */ 259 private final PropertyChangeSupport propertyChangeSupport; 260 261 /** 262 * handler for {@code process} mehtod. 263 */ 264 private AccumulativeRunnable<V> doProcess; 265 266 /** 267 * handler for progress property change notifications. 268 */ 269 private AccumulativeRunnable<Integer> doNotifyProgressChange; 270 271 private final AccumulativeRunnable<Runnable> doSubmit = getDoSubmit(); 272 273 /** 274 * Values for the {@code state} bound property. 275 * @since 1.6 276 */ 277 public enum StateValue { 278 /** 279 * Initial {@code SwingWorker} state. 280 */ 281 PENDING, 282 /** 283 * {@code SwingWorker} is {@code STARTED} 284 * before invoking {@code doInBackground}. 285 */ 286 STARTED, 287 288 /** 289 * {@code SwingWorker} is {@code DONE} 290 * after {@code doInBackground} method 291 * is finished. 292 */ 293 DONE 294 } 295 296 /** 297 * Constructs this {@code SwingWorker}. 298 */ 299 public SwingWorker() { 300 Callable<T> callable = 301 new Callable<T>() { 302 public T call() throws Exception { 303 setState(StateValue.STARTED); 304 return doInBackground(); 305 } 306 }; 307 308 future = new FutureTask<T>(callable) { 309 @Override 310 protected void done() { 311 doneEDT(); 312 setState(StateValue.DONE); 313 } 314 }; 315 316 state = StateValue.PENDING; 317 propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this); 318 doProcess = null; 319 doNotifyProgressChange = null; 320 } 321 322 /** 323 * Computes a result, or throws an exception if unable to do so. 324 * 325 * <p> 326 * Note that this method is executed only once. 327 * 328 * <p> 329 * Note: this method is executed in a background thread. 330 * 331 * 332 * @return the computed result 333 * @throws Exception if unable to compute a result 334 * 335 */ 336 protected abstract T doInBackground() throws Exception ; 337 338 /** 339 * Sets this {@code Future} to the result of computation unless 340 * it has been cancelled. 341 */ 342 public final void run() { 343 future.run(); 344 } 345 346 /** 347 * Sends data chunks to the {@link #process} method. This method is to be 348 * used from inside the {@code doInBackground} method to deliver 349 * intermediate results 350 * for processing on the <i>Event Dispatch Thread</i> inside the 351 * {@code process} method. 352 * 353 * <p> 354 * Because the {@code process} method is invoked asynchronously on 355 * the <i>Event Dispatch Thread</i> 356 * multiple invocations to the {@code publish} method 357 * might occur before the {@code process} method is executed. For 358 * performance purposes all these invocations are coalesced into one 359 * invocation with concatenated arguments. 360 * 361 * <p> 362 * For example: 363 * 364 * <pre> 365 * publish("1"); 366 * publish("2", "3"); 367 * publish("4", "5", "6"); 368 * </pre> 369 * 370 * might result in: 371 * 372 * <pre> 373 * process("1", "2", "3", "4", "5", "6") 374 * </pre> 375 * 376 * <p> 377 * <b>Sample Usage</b>. This code snippet loads some tabular data and 378 * updates {@code DefaultTableModel} with it. Note that it safe to mutate 379 * the tableModel from inside the {@code process} method because it is 380 * invoked on the <i>Event Dispatch Thread</i>. 381 * 382 * <pre> 383 * class TableSwingWorker extends 384 * SwingWorker<DefaultTableModel, Object[]> { 385 * private final DefaultTableModel tableModel; 386 * 387 * public TableSwingWorker(DefaultTableModel tableModel) { 388 * this.tableModel = tableModel; 389 * } 390 * 391 * {@code @Override} 392 * protected DefaultTableModel doInBackground() throws Exception { 393 * for (Object[] row = loadData(); 394 * ! isCancelled() && row != null; 395 * row = loadData()) { 396 * publish((Object[]) row); 397 * } 398 * return tableModel; 399 * } 400 * 401 * {@code @Override} 402 * protected void process(List<Object[]> chunks) { 403 * for (Object[] row : chunks) { 404 * tableModel.addRow(row); 405 * } 406 * } 407 * } 408 * </pre> 409 * 410 * @param chunks intermediate results to process 411 * 412 * @see #process 413 * 414 */ 415 @SafeVarargs 416 @SuppressWarnings("varargs") // Passing chunks to add is safe 417 protected final void publish(V... chunks) { 418 synchronized (this) { 419 if (doProcess == null) { 420 doProcess = new AccumulativeRunnable<V>() { 421 @Override 422 public void run(List<V> args) { 423 process(args); 424 } 425 @Override 426 protected void submit() { 427 doSubmit.add(this); 428 } 429 }; 430 } 431 } 432 doProcess.add(chunks); 433 } 434 435 /** 436 * Receives data chunks from the {@code publish} method asynchronously on the 437 * <i>Event Dispatch Thread</i>. 438 * 439 * <p> 440 * Please refer to the {@link #publish} method for more details. 441 * 442 * @param chunks intermediate results to process 443 * 444 * @see #publish 445 * 446 */ 447 protected void process(List<V> chunks) { 448 } 449 450 /** 451 * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground} 452 * method is finished. The default 453 * implementation does nothing. Subclasses may override this method to 454 * perform completion actions on the <i>Event Dispatch Thread</i>. Note 455 * that you can query status inside the implementation of this method to 456 * determine the result of this task or whether this task has been cancelled. 457 * 458 * @see #doInBackground 459 * @see #isCancelled() 460 * @see #get 461 */ 462 protected void done() { 463 } 464 465 /** 466 * Sets the {@code progress} bound property. 467 * The value should be from 0 to 100. 468 * 469 * <p> 470 * Because {@code PropertyChangeListener}s are notified asynchronously on 471 * the <i>Event Dispatch Thread</i> multiple invocations to the 472 * {@code setProgress} method might occur before any 473 * {@code PropertyChangeListeners} are invoked. For performance purposes 474 * all these invocations are coalesced into one invocation with the last 475 * invocation argument only. 476 * 477 * <p> 478 * For example, the following invokations: 479 * 480 * <pre> 481 * setProgress(1); 482 * setProgress(2); 483 * setProgress(3); 484 * </pre> 485 * 486 * might result in a single {@code PropertyChangeListener} notification with 487 * the value {@code 3}. 488 * 489 * @param progress the progress value to set 490 * @throws IllegalArgumentException is value not from 0 to 100 491 */ 492 protected final void setProgress(int progress) { 493 if (progress < 0 || progress > 100) { 494 throw new IllegalArgumentException("the value should be from 0 to 100"); 495 } 496 if (this.progress == progress) { 497 return; 498 } 499 int oldProgress = this.progress; 500 this.progress = progress; 501 if (! getPropertyChangeSupport().hasListeners("progress")) { 502 return; 503 } 504 synchronized (this) { 505 if (doNotifyProgressChange == null) { 506 doNotifyProgressChange = 507 new AccumulativeRunnable<Integer>() { 508 @Override 509 public void run(List<Integer> args) { 510 firePropertyChange("progress", 511 args.get(0), 512 args.get(args.size() - 1)); 513 } 514 @Override 515 protected void submit() { 516 doSubmit.add(this); 517 } 518 }; 519 } 520 } 521 doNotifyProgressChange.add(oldProgress, progress); 522 } 523 524 /** 525 * Returns the {@code progress} bound property. 526 * 527 * @return the progress bound property. 528 */ 529 public final int getProgress() { 530 return progress; 531 } 532 533 /** 534 * Schedules this {@code SwingWorker} for execution on a <i>worker</i> 535 * thread. There are a number of <i>worker</i> threads available. In the 536 * event all <i>worker</i> threads are busy handling other 537 * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting 538 * queue. 539 * 540 * <p> 541 * Note: 542 * {@code SwingWorker} is only designed to be executed once. Executing a 543 * {@code SwingWorker} more than once will not result in invoking the 544 * {@code doInBackground} method twice. 545 */ 546 public final void execute() { 547 getWorkersExecutorService().execute(this); 548 } 549 550 // Future methods START 551 /** 552 * {@inheritDoc} 553 */ 554 public final boolean cancel(boolean mayInterruptIfRunning) { 555 return future.cancel(mayInterruptIfRunning); 556 } 557 558 /** 559 * {@inheritDoc} 560 */ 561 public final boolean isCancelled() { 562 return future.isCancelled(); 563 } 564 565 /** 566 * {@inheritDoc} 567 */ 568 public final boolean isDone() { 569 return future.isDone(); 570 } 571 572 /** 573 * {@inheritDoc} 574 * <p> 575 * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks 576 * <i>all</i> events, including repaints, from being processed until this 577 * {@code SwingWorker} is complete. 578 * 579 * <p> 580 * When you want the {@code SwingWorker} to block on the <i>Event 581 * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>. 582 * 583 * <p> 584 * For example: 585 * 586 * <pre> 587 * class SwingWorkerCompletionWaiter implements PropertyChangeListener { 588 * private JDialog dialog; 589 * 590 * public SwingWorkerCompletionWaiter(JDialog dialog) { 591 * this.dialog = dialog; 592 * } 593 * 594 * public void propertyChange(PropertyChangeEvent event) { 595 * if ("state".equals(event.getPropertyName()) 596 * && SwingWorker.StateValue.DONE == event.getNewValue()) { 597 * dialog.setVisible(false); 598 * dialog.dispose(); 599 * } 600 * } 601 * } 602 * JDialog dialog = new JDialog(owner, true); 603 * swingWorker.addPropertyChangeListener( 604 * new SwingWorkerCompletionWaiter(dialog)); 605 * swingWorker.execute(); 606 * //the dialog will be visible until the SwingWorker is done 607 * dialog.setVisible(true); 608 * </pre> 609 * 610 * @throws CancellationException {@inheritDoc} 611 */ 612 public final T get() throws InterruptedException, ExecutionException { 613 return future.get(); 614 } 615 616 /** 617 * {@inheritDoc} 618 * <p> 619 * Please refer to {@link #get} for more details. 620 * 621 * @throws CancellationException {@inheritDoc} 622 */ 623 public final T get(long timeout, TimeUnit unit) throws InterruptedException, 624 ExecutionException, TimeoutException { 625 return future.get(timeout, unit); 626 } 627 628 // Future methods END 629 630 // PropertyChangeSupports methods START 631 /** 632 * Adds a {@code PropertyChangeListener} to the listener list. The listener 633 * is registered for all properties. The same listener object may be added 634 * more than once, and will be called as many times as it is added. If 635 * {@code listener} is {@code null}, no exception is thrown and no action is taken. 636 * 637 * <p> 638 * Note: This is merely a convenience wrapper. All work is delegated to 639 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. 640 * 641 * @param listener the {@code PropertyChangeListener} to be added 642 */ 643 public final void addPropertyChangeListener(PropertyChangeListener listener) { 644 getPropertyChangeSupport().addPropertyChangeListener(listener); 645 } 646 647 /** 648 * Removes a {@code PropertyChangeListener} from the listener list. This 649 * removes a {@code PropertyChangeListener} that was registered for all 650 * properties. If {@code listener} was added more than once to the same 651 * event source, it will be notified one less time after being removed. If 652 * {@code listener} is {@code null}, or was never added, no exception is 653 * thrown and no action is taken. 654 * 655 * <p> 656 * Note: This is merely a convenience wrapper. All work is delegated to 657 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. 658 * 659 * @param listener the {@code PropertyChangeListener} to be removed 660 */ 661 public final void removePropertyChangeListener(PropertyChangeListener listener) { 662 getPropertyChangeSupport().removePropertyChangeListener(listener); 663 } 664 665 /** 666 * Reports a bound property update to any registered listeners. No event is 667 * fired if {@code old} and {@code new} are equal and non-null. 668 * 669 * <p> 670 * This {@code SwingWorker} will be the source for 671 * any generated events. 672 * 673 * <p> 674 * When called off the <i>Event Dispatch Thread</i> 675 * {@code PropertyChangeListeners} are notified asynchronously on 676 * the <i>Event Dispatch Thread</i>. 677 * <p> 678 * Note: This is merely a convenience wrapper. All work is delegated to 679 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. 680 * 681 * 682 * @param propertyName the programmatic name of the property that was 683 * changed 684 * @param oldValue the old value of the property 685 * @param newValue the new value of the property 686 */ 687 public final void firePropertyChange(String propertyName, Object oldValue, 688 Object newValue) { 689 getPropertyChangeSupport().firePropertyChange(propertyName, 690 oldValue, newValue); 691 } 692 693 /** 694 * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}. 695 * This method is used when flexible access to bound properties support is 696 * needed. 697 * <p> 698 * This {@code SwingWorker} will be the source for 699 * any generated events. 700 * 701 * <p> 702 * Note: The returned {@code PropertyChangeSupport} notifies any 703 * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch 704 * Thread</i> in the event that {@code firePropertyChange} or 705 * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch 706 * Thread</i>. 707 * 708 * @return {@code PropertyChangeSupport} for this {@code SwingWorker} 709 */ 710 public final PropertyChangeSupport getPropertyChangeSupport() { 711 return propertyChangeSupport; 712 } 713 714 // PropertyChangeSupports methods END 715 716 /** 717 * Returns the {@code SwingWorker} state bound property. 718 * 719 * @return the current state 720 */ 721 public final StateValue getState() { 722 /* 723 * DONE is a speacial case 724 * to keep getState and isDone is sync 725 */ 726 if (isDone()) { 727 return StateValue.DONE; 728 } else { 729 return state; 730 } 731 } 732 733 /** 734 * Sets this {@code SwingWorker} state bound property. 735 * @param state the state to set 736 */ 737 private void setState(StateValue state) { 738 StateValue old = this.state; 739 this.state = state; 740 firePropertyChange("state", old, state); 741 } 742 743 /** 744 * Invokes {@code done} on the EDT. 745 */ 746 private void doneEDT() { 747 Runnable doDone = 748 new Runnable() { 749 public void run() { 750 done(); 751 } 752 }; 753 if (SwingUtilities.isEventDispatchThread()) { 754 doDone.run(); 755 } else { 756 doSubmit.add(doDone); 757 } 758 } 759 760 761 /** 762 * returns workersExecutorService. 763 * 764 * returns the service stored in the appContext or creates it if 765 * necessary. 766 * 767 * @return ExecutorService for the {@code SwingWorkers} 768 */ 769 private static synchronized ExecutorService getWorkersExecutorService() { 770 final AppContext appContext = AppContext.getAppContext(); 771 ExecutorService executorService = 772 (ExecutorService) appContext.get(SwingWorker.class); 773 if (executorService == null) { 774 //this creates daemon threads. 775 ThreadFactory threadFactory = 776 new ThreadFactory() { 777 final ThreadFactory defaultFactory = 778 Executors.defaultThreadFactory(); 779 public Thread newThread(final Runnable r) { 780 Thread thread = 781 defaultFactory.newThread(r); 782 thread.setName("SwingWorker-" 783 + thread.getName()); 784 thread.setDaemon(true); 785 return thread; 786 } 787 }; 788 789 executorService = 790 new ThreadPoolExecutor(MAX_WORKER_THREADS, MAX_WORKER_THREADS, 791 10L, TimeUnit.MINUTES, 792 new LinkedBlockingQueue<Runnable>(), 793 threadFactory); 794 appContext.put(SwingWorker.class, executorService); 795 796 // Don't use ShutdownHook here as it's not enough. We should track 797 // AppContext disposal instead of JVM shutdown, see 6799345 for details 798 final ExecutorService es = executorService; 799 appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME, 800 new PropertyChangeListener() { 801 @Override 802 public void propertyChange(PropertyChangeEvent pce) { 803 boolean disposed = (Boolean)pce.getNewValue(); 804 if (disposed) { 805 final WeakReference<ExecutorService> executorServiceRef = 806 new WeakReference<ExecutorService>(es); 807 final ExecutorService executorService = 808 executorServiceRef.get(); 809 if (executorService != null) { 810 AccessController.doPrivileged( 811 new PrivilegedAction<Void>() { 812 public Void run() { 813 executorService.shutdown(); 814 return null; 815 } 816 } 817 ); 818 } 819 } 820 } 821 } 822 ); 823 } 824 return executorService; 825 } 826 827 private static final Object DO_SUBMIT_KEY = new StringBuilder("doSubmit"); 828 private static AccumulativeRunnable<Runnable> getDoSubmit() { 829 synchronized (DO_SUBMIT_KEY) { 830 final AppContext appContext = AppContext.getAppContext(); 831 Object doSubmit = appContext.get(DO_SUBMIT_KEY); 832 if (doSubmit == null) { 833 doSubmit = new DoSubmitAccumulativeRunnable(); 834 appContext.put(DO_SUBMIT_KEY, doSubmit); 835 } 836 @SuppressWarnings("unchecked") 837 AccumulativeRunnable<Runnable> tmp = (AccumulativeRunnable<Runnable>) doSubmit; 838 return tmp; 839 } 840 } 841 private static class DoSubmitAccumulativeRunnable 842 extends AccumulativeRunnable<Runnable> implements ActionListener { 843 private static final int DELAY = 1000 / 30; 844 @Override 845 protected void run(List<Runnable> args) { 846 for (Runnable runnable : args) { 847 runnable.run(); 848 } 849 } 850 @Override 851 protected void submit() { 852 Timer timer = new Timer(DELAY, this); 853 timer.setRepeats(false); 854 timer.start(); 855 } 856 public void actionPerformed(ActionEvent event) { 857 run(); 858 } 859 } 860 861 @SuppressWarnings("serial") // JDK-implementation class 862 private class SwingWorkerPropertyChangeSupport 863 extends PropertyChangeSupport { 864 SwingWorkerPropertyChangeSupport(Object source) { 865 super(source); 866 } 867 @Override 868 public void firePropertyChange(final PropertyChangeEvent evt) { 869 if (SwingUtilities.isEventDispatchThread()) { 870 super.firePropertyChange(evt); 871 } else { 872 doSubmit.add( 873 new Runnable() { 874 public void run() { 875 SwingWorkerPropertyChangeSupport.this 876 .firePropertyChange(evt); 877 } 878 }); 879 } 880 } 881 } 882 }