【JAVA线程】SwingWorker的用法

Java-使用SwingWorker在工作线程更新主线程UI Swing注意事项: ​ 在Java编程中会用到Swing来绘制界面,但是由于Swing线程不安全,所以如果进行一些在子线程中动态更新UI的操作时,会出现子进程结束返回时UI才变化的情况(例如在子线程中收发文件同时更新UI进度条显示,如果在子线程中更新UI会造成只有在文件传输完成的时候进度才从0一下跳到100的状况)。所以在进行Swing更新操作的时候,要注意: 1.不要阻塞UI绘制进程(将耗时操作放在线程中进行)2.不要在线程中对UI进行更新。 如果需要在子线程中动态更新UI的话,可以使用Swingwo 阅读详情

        Swing应用程序员常见的错误是误用Swing事件调度线程(Event DispatchThread,EDT)。他们要么从非UI线程访问UI组件;要么不考虑事件执行顺序;要么不使用独立任务线程而在EDT线程上执行耗时任务,结果使编写的应用程序变得响应迟钝、速度很慢。耗时计算和输入/输出(IO)密集型任务不应放在SwingEDT上运行。发现这种问题的代码并不容易,但Java SE6提供了javax.swing.SwingWorker类,使修正这种代码变得更容易。

        使用SwingWorker,程序能启动一个任务线程来异步查询,并马上返回EDT线程,允许EDT继续执行后续的UI事件。

 

        SwingWorker类帮你管理任务线程和EDT之间的交互,尽管SwingWorker不能解决并发线程中遇到的所有问题,但的确有助于分离SwingEDT和任务线程,使它们各负其责:对于EDT来说,就是绘制和更新界面,并响应用户输入;对于任务线程来说,就是执行和界面无直接关系的耗时任务和I/O密集型操作。


SwingWorker结构

       SwingWoker实现了java.util.concurrent.RunnableFuture接口。RunnableFuture接口是Runnable和Future两个接口的简单封装。

因为实现了Runnable,所以有run方法,调用FutureTask.run()

因为实现了Future,所以有:


  1. public abstract class SwingWorker<T, V> implements RunnableFuture<T> {  
  2.     //FutureTask  
  3.     private final FutureTask<T> future;  
  4.     public final void run() {  
  5.         future.run();  
  6.     }  
  7.   
  8.     public SwingWorker() {  
  9.         Callable<T> callable =   
  10.                 new Callable<T>() {  
  11. //1. 任务线程一创建就处于PENDING状态  
  12.                     public T call() throws Exception {  
  13. //2. 当doInBackground方法开始时,任务线程就进入STARTED状态  
  14.                     setState(StateValue.STARTED);    
  15.                         return doInBackground();  
  16.                     }  
  17.                 };  
  18.   
  19.         //FutureTask  
  20.         future = new FutureTask<T>(callable) {  
  21.   
  22.                        @Override  
  23.                        protected void done() {  
  24.                            doneEDT();  
  25. //3. 当doInBackground方法完成后,任务线程就处于DONE状态  
  26.                            setState(StateValue.DONE);   
  27.                        }  
  28.                    };  
  29.   
  30.        state = StateValue.PENDING;  
  31.        propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);  
  32.        doProcess = null;  
  33.        doNotifyProgressChange = null;  
  34.     }  
  35.   
  36. }  

   

       SwingWorker有两个类型参数:T及VT是doInBackground和get方法的返回类型;V是publish和process方法要处理的数据类型

       SwingWorker实例不可复用,每次执行任务必须生成新的实例。


doInBackground和get和done

  1. //1、doInBackground  
  2. protected abstract T doInBackground() throws Exception ;  
  3. //2、get --不可重写  
  4. public final T get() throws InterruptedException, ExecutionException {  
  5.     return future.get();  
  6. }  
  7. //3、done  
  8. protected void done() {  
  9. }  
  10.   
  11.   
  12. /** 
  13.  * Invokes {@code done} on the EDT. 
  14.  */  
  15. private void doneEDT() {  
  16.     Runnable doDone =   
  17.         new Runnable() {  
  18.             public void run() {  
  19.                 done();  
  20.             }  
  21.         };   
  22.     //SwingWorker在EDT上激活done()  
  23.     if (SwingUtilities.isEventDispatchThread()) {   
  24.         doDone.run();  
  25.     } else {  
  26.         doSubmit.add(doDone);  
  27.     }  
  28. }  


       doInBackground方法作为任务线程的一部分执行,它负责完成线程的基本任务,并以返回值来作为线程的执行结果。继承类须覆盖该方法并确保包含或代理任务线程的基本任务。不要直接调用该方法,应使用任务对象的execute方法来调度执行。

       在获得执行结果后应使用SwingWorker的get方法获取doInBackground方法的结果。可以在EDT上调用get方法,但该方法将一直处于阻塞状态,直到任务线程完成。最好只有在知道结果时才调用get方法,这样用户便不用等待。为防止阻塞,可以使用isDone方法来检验doInBackground是否完成。另外调用方法get(longtimeout, TimeUnitunit)将会一直阻塞直到任务线程结束或超时。get获取任务结果的最好地方是在done方法内

       在doInBackground方法完成之后,SwingWorker调用done方法。如果任务需要在完成后使用线程结果更新GUI组件或者做些清理工作,可覆盖done方法来完成它们。这儿是调用get方法的最好地方,因为此时已知道线程任务完成了,SwingWorker在EDT上激活done方法,因此可以在此方法内安全地和任何GUI组件交互

【例】

  1. SwingWorker testWorker = new SwingWorker<Icon , Void>(){  
  2.       @Override  
  3.        protected Icon doInBackground() throws Exception {  
  4.         Icon icon = retrieveImage(strImageUrl);   
  5.             return icon;   
  6.        }   
  7.   
  8.        protected void done(){   
  9.        //没有必要用invokeLater!因为done()本身是在EDT中执行的   
  10.            SwingUtilities.invokeLater(new Runnable(){   
  11.             @Override   
  12.             public void run() {  
  13.             Icon icon= get();  
  14.                         lblImage.setIcon(icon); //lblImage可通过构造函数传入  
  15.                 }             
  16. }  
  17. //execute方法是异步执行,它立即返回到调用者。在execute方法执行后,EDT立即继续执行  
  18. testWorker.execute();  


  • 指定Icon作为doInBackground和get方法的返回类型
  • 因为并不产生任何中间数据,所以指定Void类型作为中间结果类型。


publish和process

       SwingWorker在doInBackground方法结束后才产生最后结果,但任务线程也可以产生和公布中间数据。有时没必要等到线程完成就可以获得中间结果。

      中间结果是任务线程在产生最后结果之前就能产生的数据。当任务线程执行时,它可以发布类型为V的中间结果,通过覆盖process方法来处理中间结果。

      任务对象的父类会在EDT线程上激活process方法,因此在process方法中程序可以安全的更新UI组件

  1.    
  2. //SwingWorker.publish  
  3.    protected final void publish(V... chunks) {  
  4.         synchronized (this) {  
  5.             if (doProcess == null) {  
  6.                 doProcess = new AccumulativeRunnable<V>() {  
  7.                     @Override  
  8.                     public void run(List<V> args) {  
  9.                         //调用process  
  10.                         process(args);   
  11.                     }  
  12.                     @Override  
  13.                     protected void submit() {  
  14.                         doSubmit.add(this);  
  15.                     }  
  16.                 };  
  17.             }  
  18.         }  
  19.         doProcess.add(chunks);  
  20.     }  
  21.   
  22.     //SwingWorker.process 在EDT中调用  
  23.     protected void process(List<V> chunks) {  
  24.     }  

当从任务线程调用publish方法时,SwingWorker类调度process方法。有意思的是process方法是在EDT上面执行,这意味着可以同Swing组件和其模型直接交互。


例如可让publish处理Icon类型的数据;则doInBackground对应应该返回List<Icon>类型

       使用publish方法来发布要处理的中间数据,当ImageSearcher线程下载缩略图时,它会随着下载而更新图片信息列表,还会发布每一批图像信息,以便UI能在图片数据到达时显示这些图片。

       如果SwingWorker通过publish发布了一些数据,那么也应该实现process方法来处理这些中间结果,任务对象的父类会在EDT线程上激活process方法,因此在此方法中程序可以安全的更新UI组件。

【例】

  1. private void retrieveAndProcessThumbnails(List<ImageInfo> infoList) {  
  2.   for (int x=0; x <infoList.size() && !isCancelled(); ++x) {             
  3.     ImageInfo info = infoList.get(x);  
  4.     String strImageUrl = String.format("%s/%s/%s_%s_s.jpg",  
  5.     IMAGE_URL, info.getServer(), info.getId(), info.getSecret());  
  6.     Icon thumbNail = retrieveThumbNail(strImageUrl);  
  7.     info.setThumbnail(thumbNail);  
  8.     //发布中间结果  
  9.     publish(info);   
  10.     setProgress(100 * (x+1)/infoList.size());  
  11.   }  
  12. }     
  13. /** 
  14.  * Process is called as a result of this worker thread's calling the 
  15.  * publish method. This method runs on the event dispatch thread. 
  16.  * 
  17.  * As image thumbnails are retrieved, the worker adds them to the 
  18.  * list model. 
  19.  * 
  20.  */  
  21. @Override  
  22. protected void process(List<ImageInfo> infoList) {  
  23.   for(ImageInfo info: infoList) {  
  24.     if (isCancelled()) { //见下节  
  25.       break;  
  26.     }  
  27.     //处理中间结果  
  28.     model.addElement(info);  
  29.   }       
  30. }  


cancel和isCancelled


  1. public final boolean cancel(boolean mayInterruptIfRunning) {  
  2.     return future.cancel(mayInterruptIfRunning);  
  3. }  
  4.   
  5. /** 
  6.  * {@inheritDoc} 
  7.  */  
  8. public final boolean isCancelled() {  
  9.     return future.isCancelled();  
  10. }  

如果想允许程序用户取消任务,实现代码要在SwingWorker子类中周期性地检查取消请求。调用isCancelled方法来检查是否有取消请求。检查的时机主要是:

  • doInBackground方法的子任务在获取每个缩略图之前
  • process方法中在更新GUI列表模型之前
  • done方法中在更新GUI列表模型最终结果之前

【例】判断是否被取消(见上例)

【例】取消

可以通过调用其cancel方法取消SwingWorker线程

  1. private void searchImages(String strSearchText, int page) {  
  2.   
  3.   if (searcher != null && !searcher.isDone()) {  
  4.     // Cancel current search to begin a new one.  
  5.     // You want only one image search at a time.  
  6.     //检查现有线程是否正在运行,如果正在运行则调用cancel来取消  
  7.     searcher.cancel(true);  
  8.     searcher = null;  
  9.   }  
  10.   ...  
  11.   // Provide the list model so that the ImageSearcher can publish  
  12.   // images to the list immediately as they are available.  
  13.   searcher = new ImageSearcher(listModel, API_KEY, strEncodedText, page);  
  14.   searcher.addPropertyChangeListener(listenerMatchedImages);  
  15.   progressMatchedImages.setIndeterminate(true);  
  16.   // Start the search!  
  17.   searcher.execute();  
  18.   // This event thread continues immediately here without blocking.  
  19. }   


setProgress和getProgress

任务对象有一个进度属性,随着任务进展时,可以将这个属性从0更新到100标识任务进度。当你在任务实例内处理这些信息时,你可以调用setProgress方法来更新这个属性。

当该属性发生变化时,任务通知处理器进行处理。(?)

【例】

  1. //javax.imageio.ImageReader reader  
  2.   
  3. reader.addIIOReadProgressListener(new IIOReadProgressListener() {  
  4.   ...             
  5.   public void imageProgress(ImageReader source, float percentageDone) {  
  6.     setProgress((int) percentageDone);  
  7.   }  
  8.   public void imageComplete(ImageReader source) {  
  9.     setProgress(100);  
  10.   }  
  11. });   

客户端调用1、更新进度条事件处理

  1. /** 
  2.  * ProgressListener listens to "progress" property changes  
  3.    in the SwingWorkers that search and load images. 
  4.  */  
  5. class ProgressListener implements PropertyChangeListener {  
  6.   // Prevent creation without providing a progress bar.  
  7.   private ProgressListener() {}    
  8.   ProgressListener(JProgressBar progressBar) {  
  9.     this.progressBar = progressBar;  
  10.     this.progressBar.setValue(0);  
  11.   }    
  12.   public void propertyChange(PropertyChangeEvent evt) {  
  13.     String strPropertyName = evt.getPropertyName();  
  14.     if ("progress".equals(strPropertyName)) {  
  15.       progressBar.setIndeterminate(false);  
  16.       int progress = (Integer)evt.getNewValue();  
  17.       progressBar.setValue(progress);  
  18.     }  
  19.   }    
  20.   private JProgressBar progressBar;  
  21. }   

客户端调用2、添加监听

  1. private void listImagesValueChanged(ListSelectionEvent evt) {  
  2.   ...  
  3.   ImageInfo info = (ImageInfo) listImages.getSelectedValue();  
  4.   String id = info.getId();  
  5.   String server = info.getServer();  
  6.   String secret = info.getSecret();  
  7.   // No need to search an invalid thumbnail image  
  8.   if (id == null || server == null || secret == null) {  
  9.     return;  
  10.   }  
  11.   String strImageUrl = String.format(IMAGE_URL_FORMAT, server, id, secret);  
  12.   retrieveImage(strImageUrl);  
  13.   ...  
  14. }      
  15. private void retrieveImage(String imageUrl) {  
  16.   // SwingWorker,不可复用  
  17.   ImageRetriever imgRetriever = new ImageRetriever(lblImage, imageUrl);  
  18.   progressSelectedImage.setValue(0);  
  19.   // Listen for changes in the "progress" property.  
  20.   // You can reuse the listener even though the worker thread will be a new SwingWorker.  
  21.   imgRetriever.addPropertyChangeListener(listenerSelectedImage);    
  22.   progressSelectedImage.setIndeterminate(true);    
  23.   // Tell the worker thread to begin with this asynchronous method.  
  24.   imgRetriever.execute();  
  25.   // This event thread continues immediately here without blocking.    
  26. }       



参考:

http://blog.sina.com.cn/s/blog_4b6047bc010007so.html

使用SwingWorker类优化耗时操作 为了解决这个问题,Java提供了SwingWorker类,它可以帮助我们在后台线程中执行耗时操作,从而保持用户界面的流畅性。首先,我们需要创建一个继承自SwingWorker类的子类,该子类将负责执行我们的耗时操作。SwingWorker类是javax.swing包中的一个抽象类,它提供了一个方便的方式来执行后台任务,并在任务执行完成后更新用户界面。总之,SwingWorker类是一个非常实用的工具,在开发Java应用程序时可以帮助我们优化耗时操作,提升用户体验。使用SwingWorker类优化耗时操作。 阅读详情

相关推荐

Swing线程的深入理解和SwingWorker基础知识介绍

Swing线程的深入理解和SwingWorker基础知识介绍

Java线程SwingWorker用法

Swing应用程序员常见的错误是误用Swing事件调度线程(Event DispatchThread,EDT)。他们要么从非UI线程访问UI组件;要么不考虑事件执行顺序;要么不使用独立任务线程而在EDT线程上执行耗时任务,结果使编写的应用程序变得响应迟钝、速度很慢。耗时计算和输入/输出(IO)密集型任务不应放在SwingEDT上运行。发现这种问题的代码并不容易,但Java SE6提供了java

Alpha's 学习笔记 4万+

JavaSwing编程中使用SwingWorker线程模式及顶层容器

主要介绍了在JavaSwing编程中使用SwingWorker线程模式及顶层容器的方法,适用于客户端图形化界面软件的开发,需要的朋友可以参考下

swingworker_使用SwingWorkerJava Swing中的多线程

swingworker 如果要使用Swing用J​​ava编写桌面或Java Web Start程序,您可能会觉得需要通过创建自己的线程在后台运行某些程序。 没有什么可以阻止您在Swing中使用标准的多线程技术,并且需要遵循通常的注意事项。 如果有多个线程访问相同的变量,则需要使用同步方法或代码块(或诸如AtomicInteger或ArrayBlockingQueue之类的线程安全类)。 ...

最佳 Java 编程 1771

Java Swing编程中需要注意的线程调度及SwingWorker用法

Java Swing 被设计成了一个单线程模型,这有很多原因,包括开发成本和同步Swing的复杂性--这都会造成一个迟钝的API。为了达到单线程模型,有一个专门的线程用于和Swing组件交互,就是Swing事件调度线程(Event DispatchThread,EDT)。所以在Swing中执行耗时任务时,要在一个新线程中执行,不能阻塞EDT线程,否则会造成swing界面的不响应,即卡死。Swing

Seven's Rainbow Paradise 2479

如何关闭一个swingworker线程

如题,一般程序中利用swingworker启动一个线程都是直接用匿名方法直接 new swingworker(){ protected Void doInBackground() throws Exception { while(true){ 内容。。。 } } }.execute(); 一般都是这样的,而且一次根据需要可能会new出N个线程来,全都是匿名的,那么我现在在窗口dis

juce的专栏 2184

(转)Swing中的并发-使用SwingWorker线程模式

http://zhangjunhd.blog.51cto.com/113473/34727  http://pope945.iteye.com/blog/263949  http://202.201.112.11/jpk/apply/teacher/preface/53/api/javax/swing/SwingWorker.html  个人觉得文笔最好思路最清晰来龙去脉交代得最明白也最浅显...

weixin_34376986的博客 173

Java程序执行超时——Future接口介绍

Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现。 Future接口是Java标准API的一部分,在java.util.concurrent包中。Future接口是Java线程Future模式的实 现,可以来进行异步计算。 Future模式可以这样来描述:我有一个任务,提交给了FutureFuture替我完

z69183787的专栏 2060

使用SwingWorkerJava Swing中的多线程

如果要使用Swing用J​​ava编写桌面或Java Web Start程序,您可能会觉得需要通过创建自己的线程在后台运行某些东西。 没有什么可以阻止您在Swing中使用标准的多线程技术,并且需要遵循通常的注意事项。 如果您有多个线程访问相同的变量,则需要使用同步方法或代码块(或诸如AtomicInteger或ArrayBlockingQueue之类的线程安全类)。 但是,对于那些粗心...

最佳 Java 编程 1822

java swingworker_JavaSwing编程中使用SwingWorker线程模式及顶层容器

使用SwingWorker线程模式谨慎地使用并发机制对Swing开发人员来说非常重要。一个好的Swing程序使用并发机制来创建不会失去响应的用户接口-不管是什么样的用户交互,程序总能够对其给出响应。创建一个有响应的程序,开发人员必须学会如何在Swing框架中使用多线程。一个Swing开发人员将会与下面几类线程打交道:(1)Initial threads(初始线程),此类线程将执行初始化应用代码。(...

weixin_30340109的博客 797

Java swingSwingWorker开启子线程后台执行任务

Java swingJava swing通过SwingWorker线程后台执行任务可与GUI实时交互核心代码示例代码 在主线程直接执行方法会阻塞主线程,方法执行完之前,界面会卡住无法操作等情况,这时候,我们需要使用SwingWorker类来实现后台子线程执行。 核心代码 private void startPrint() { //调用方法 startPrint().execute(); } private SwingWorker<Void, Void> s

猫巳的博客 1027

SwingWorker用法

[转:http://blog.csdn.net/vking_wang/article/details/8994882] Swing应用程序员常见的错误是误用Swing事件调度线程(Event DispatchThread,EDT)。他们要么从非UI线程访问UI组件;要么不考虑事件执行顺序;要么不使用独立任务线程而在EDT线程上执行耗时任务,结果使编写的应用程序变得响应迟钝、速度很慢。耗时计算...

lvjun106 653

Java界面中使用SwingWorker

Java Swing GUI程序启动时,JVM会启动多个线程Swing并不是线程安全的,如果处理不当Swing程序会反应迟钝,JavaSE6开始引用SwingWorker能够改善Swing程序结构,提高界面的灵活性。 事件派发线程当界面程序启动时,会调用界面的主方法 Java.awt.EventQueue.invokeLater(new Runnable(){ public

Mr_Yao 4605

javaSwingWorker类_如何在Java中使用SwingWorker

通常,SwingWorker用于在Swing中执行长时间运行的任务。在事件分派线程(EDT)上运行长时间运行的任务可能导致GUI锁定,所以做的事情之一是使用SwingUtilities.invokeLater和invokeAndWait,它保持GUI响应,在运行之前优先考虑其他AWT事件期望的任务(以Runnable的形式)。但是,SwingUtilities的问题是它不允许从执行的Runnabl...

weixin_32715349的博客 632

java:使用SwingWorker类完成耗时操作(附带源码)

java:使用SwingWorker类完成耗时操作(附带源码)

欢迎来到我的博客!这里是一个专注于计算机技术的分享平台,涵盖编程开发、算法研究、系统架构、软件工程等多个领域。无论你是初学者还是资深开发者,都能在这里找到有价值的内容。 1097

Swing中的并发-使用SwingWorker线程模式

本文介绍Java SE 6中的SwingWorker线程工作模式,翻译Concurrency in Swing([url]http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html[/url])。

hjf19790118的专栏 1242
上一篇: 【JAVA实现】用Logistic回归进行分类
下一篇: 【JAVA设计模式】简单工厂模式(Simple Factory Pattern)
C_son
博客等级 码龄12年 70粉丝 20原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值