博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
方法线程SwingWorker的用法
阅读量:6278 次
发布时间:2019-06-22

本文共 8836 字,大约阅读时间需要 29 分钟。

最近使用开发的过程中出现了一个小问题,顺便记录一下原因和方法--方法线程

        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,所以有:

    方法和线程

public abstract class SwingWorker
implements RunnableFuture
{ //FutureTask private final FutureTask
future; public final void run() { future.run(); } public SwingWorker() { Callable
callable = new Callable
() {//1. 任务线程一创立就处于PENDING状态 public T call() throws Exception {//2. 当doInBackground方法开始时,任务线程就进入STARTED状态 setState(StateValue.STARTED); return doInBackground(); } }; //FutureTask future = new FutureTask
(callable) { @Override protected void done() { doneEDT();//3. 当doInBackground方法完成后,任务线程就处于DONE状态 setState(StateValue.DONE); } }; state = StateValue.PENDING; propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this); doProcess = null; doNotifyProgressChange = null; }}

  

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

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

    

    

doInBackground和get和done

//1、doInBackground    protected abstract T doInBackground() throws Exception ;    //2、get --不可重写    public final T get() throws InterruptedException, ExecutionException {        return future.get();    }    //3、done    protected void done() {    }    /**     * Invokes {@code done} on the EDT.     */    private void doneEDT() {        Runnable doDone =             new Runnable() {                public void run() {                    done();                }            };         //SwingWorker在EDT上激活done()        if (SwingUtilities.isEventDispatchThread()) {             doDone.run();        } else {            doSubmit.add(doDone);        }    }

       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组件交互

    【例】

SwingWorker testWorker = new SwingWorker
(){ @Override protected Icon doInBackground() throws Exception { Icon icon = retrieveImage(strImageUrl); return icon; } protected void done(){ //没有必要用invokeLater!因为done()本身是在EDT中执行的 SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { Icon icon= get(); lblImage.setIcon(icon); //lblImage可通过构造函数传入 } }//execute方法是异步执行,它当即返回到调用者。在execute方法执行后,EDT当即继续执行testWorker.execute();

    

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

    

    

publish和process

       SwingWorker在doInBackground方法结束后才产生最后结果,但任务线程也可以产生和颁布旁边数据。偶然没必要等到线程完成就可以取得旁边结果。

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

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

//SwingWorker.publish   protected final void publish(V... chunks) {        synchronized (this) {            if (doProcess == null) {                doProcess = new AccumulativeRunnable
() { @Override public void run(List
args) { //调用process  process(args); } @Override protected void submit() { doSubmit.add(this); } }; } } doProcess.add(chunks); }//SwingWorker.process 在EDT中调用    protected void process(List
chunks) {    }
    每日一道理
俄国作家契诃夫说:“有大狗,有小狗,小狗不该因为大狗的存在而心慌意乱。所有的狗都应该叫,就让他各自用上帝给他的声音。

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

    

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

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

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

    【例】

private void retrieveAndProcessThumbnails(List
infoList) { for (int x=0; x
infoList) { for(ImageInfo info: infoList) { if (isCancelled()) { //见下节 break; } //处置旁边结果 model.addElement(info); } }

    

cancel和isCancelled

    

public final boolean cancel(boolean mayInterruptIfRunning) {        return future.cancel(mayInterruptIfRunning);    }    /**     * {@inheritDoc}     */    public final boolean isCancelled() {        return future.isCancelled();    }

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

    

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

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

    【例】取消

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

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

    

setProgress和getProgress

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

    当该属性发生变化时,任务通知处置器停止处置。(?)

    【例】

//javax.imageio.ImageReader readerreader.addIIOReadProgressListener(new IIOReadProgressListener() {  ...             public void imageProgress(ImageReader source, float percentageDone) {    setProgress((int) percentageDone);  }  public void imageComplete(ImageReader source) {    setProgress(100);  }});

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

/** * ProgressListener listens to "progress" property changes    in the SwingWorkers that search and load images. */class ProgressListener implements PropertyChangeListener {  // Prevent creation without providing a progress bar.  private ProgressListener() {}    ProgressListener(JProgressBar progressBar) {    this.progressBar = progressBar;    this.progressBar.setValue(0);  }    public void propertyChange(PropertyChangeEvent evt) {    String strPropertyName = evt.getPropertyName();    if ("progress".equals(strPropertyName)) {      progressBar.setIndeterminate(false);      int progress = (Integer)evt.getNewValue();      progressBar.setValue(progress);    }  }    private JProgressBar progressBar;}

    客户端调用2、添加监听

private void listImagesValueChanged(ListSelectionEvent evt) {  ...  ImageInfo info = (ImageInfo) listImages.getSelectedValue();  String id = info.getId();  String server = info.getServer();  String secret = info.getSecret();  // No need to search an invalid thumbnail image  if (id == null || server == null || secret == null) {    return;  }  String strImageUrl = String.format(IMAGE_URL_FORMAT, server, id, secret);  retrieveImage(strImageUrl);  ...}    private void retrieveImage(String imageUrl) {  // SwingWorker,不可复用  ImageRetriever imgRetriever = new ImageRetriever(lblImage, imageUrl);  progressSelectedImage.setValue(0);  // Listen for changes in the "progress" property.  // You can reuse the listener even though the worker thread will be a new SwingWorker.  imgRetriever.addPropertyChangeListener(listenerSelectedImage);    progressSelectedImage.setIndeterminate(true);    // Tell the worker thread to begin with this asynchronous method.  imgRetriever.execute();  // This event thread continues immediately here without blocking.  }

    

    参考:

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

    

文章结束给大家分享下程序员的一些笑话语录: IBM和波音777

  波音777是有史以来第一架完全在电脑虚拟现实中设计制造的飞机,所用的设备完全由IBM公司所提供。试飞前,波音公司的总裁非常热情的邀请IBM的技术主管去参加试飞,可那位主管却说道:“啊,非常荣幸,可惜那天是我妻子的生日,So..”..
  波音公司的总载一听就生气了:“胆小鬼,我还没告诉你试飞的日期呢!”

--------------------------------- 原创文章 By

方法和线程
---------------------------------

你可能感兴趣的文章
匿名函数
查看>>
大数加法
查看>>
python第二天
查看>>
Kaggle(一):房价预测
查看>>
Android开发历程_10(LayoutAnimationController的初步使用)
查看>>
apache https 伪静态
查看>>
FileUtil
查看>>
[CQOI 2014] 数三角形 & 机械排序臂
查看>>
用程序实现全排列(二)
查看>>
CF 917A The Monster 【括号匹配】
查看>>
113. 路径总和 II
查看>>
Python基础--day02
查看>>
盘点让人崩溃的表单样式之 (行列置换)
查看>>
iphone中Button的currentTitle和titleLabel.text的使用
查看>>
linux下的三种解压文件的命令?
查看>>
JDeveloper 滚轮不受控制
查看>>
恒生电子2018.10企业招聘题目
查看>>
UrlReWriter 在web.config中简单的配置
查看>>
Spring MVC 学习总结(三)——请求处理方法Action详解
查看>>
windows2003 IIS6 部署MVC3和MVC4程序
查看>>