针对用户界面的多线程

现在,我们也许能用一个线程解决在Counter1.java中出现的问题。采用的一个技巧便是在一个线程的run()方法中放置“子任务”——亦即位于go()内的循环。一旦用户按下Start按钮,线程就会启动,但马上结束线程的创建。这样一来,尽管线程仍在运行,但程序的主要工作却能得以继续(等候并响应用户界面的事件)。下面是具体的代码:
 

//: Counter2.java
// A responsive user interface with threads
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

class SeparateSubTask extends Thread {
  private int count = 0;
  private Counter2 c2;
  private boolean runFlag = true;
  public SeparateSubTask(Counter2 c2) {
    this.c2 = c2;
    start();
  }
  public void invertFlag() { runFlag = !runFlag;}
  public void run() {
    while (true) {
     try {
      sleep(100);
     } catch (InterruptedException e){}
     if(runFlag)
       c2.t.setText(Integer.toString(count++));
    }
  }
} 

public class Counter2 extends Applet {
  TextField t = new TextField(10);
  private SeparateSubTask sp = null;
  private Button
    onOff = new Button("Toggle"),
    start = new Button("Start");
  public void init() {
    add(t);
    start.addActionListener(new StartL());
    add(start);
    onOff.addActionListener(new OnOffL());
    add(onOff);
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(sp == null)
        sp = new SeparateSubTask(Counter2.this);
    }
  }
  class OnOffL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(sp != null)
        sp.invertFlag();
    }
  }
  public static void main(String[] args) {
    Counter2 applet = new Counter2();
    Frame aFrame = new Frame("Counter2");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(300,200);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~

现在,Counter2变成了一个相当直接的程序,它的唯一任务就是设置并管理用户界面。但假若用户现在按下Start按钮,却不会真正调用一个方法。此时不是创建类的一个线程,而是创建SeparateSubTask,然后继续Counter2事件循环。注意此时会保存SeparateSubTask的句柄,以便我们按下onOff按钮的时候,能正常地切换位于SeparateSubTask内部的runFlag(运行标志)。随后那个线程便可启动(当它看到标志的时候),然后将自己中止(亦可将SeparateSubTask设为一个内部类来达到这一目的)。
SeparateSubTask类是对Thread的一个简单扩展,它带有一个构建器(其中保存了Counter2句柄,然后通过调用start()来运行线程)以及一个run()——本质上包含了Counter1.java的go()内的代码。由于SeparateSubTask知道自己容纳了指向一个Counter2的句柄,所以能够在需要的时候介入,并访问Counter2的TestField(文本字段)。
按下onOff按钮,几乎立即能得到正确的响应。当然,这个响应其实并不是“立即”发生的,它毕竟和那种由“中断”驱动的系统不同。只有线程拥有CPU的执行时间,并注意到标记已发生改变,计数器才会停止。

1. 用内部类改善代码
下面说说题外话,请大家注意一下SeparateSubTask和Counter2类之间发生的结合行为。SeparateSubTask同Counter2“亲密”地结合到了一起——它必须持有指向自己“父”Counter2对象的一个句柄,以便自己能回调和操纵它。但两个类并不是真的合并为单独一个类(尽管在下一节中,我们会讲到Java确实提供了合并它们的方法),因为它们各自做的是不同的事情,而且是在不同的时间创建的。但不管怎样,它们依然紧密地结合到一起(更准确地说,应该叫“联合”),所以使程序代码多少显得有些笨拙。在这种情况下,一个内部类可以显著改善代码的“可读性”和执行效率:
 

//: Counter2i.java
// Counter2 using an inner class for the thread
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class Counter2i extends Applet {
  private class SeparateSubTask extends Thread {
    int count = 0;
    boolean runFlag = true;
    SeparateSubTask() { start(); }
    public void run() {
      while (true) {
       try {
        sleep(100);
       } catch (InterruptedException e){}
       if(runFlag)
         t.setText(Integer.toString(count++));
      }
    }
  }
  private SeparateSubTask sp = null;
  private TextField t = new TextField(10);
  private Button
    onOff = new Button("Toggle"),
    start = new Button("Start");
  public void init() {
    add(t);
    start.addActionListener(new StartL());
    add(start);
    onOff.addActionListener(new OnOffL());
    add(onOff);
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(sp == null)
        sp = new SeparateSubTask();
    }
  }
  class OnOffL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(sp != null)
        sp.runFlag = !sp.runFlag; // invertFlag();
    }
  }
  public static void main(String[] args) {
    Counter2i applet = new Counter2i();
    Frame aFrame = new Frame("Counter2i");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(300,200);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~

这个SeparateSubTask名字不会与前例中的SeparateSubTask冲突——即使它们都在相同的目录里——因为它已作为一个内部类隐藏起来。大家亦可看到内部类被设为private(私有)属性,这意味着它的字段和方法都可获得默认的访问权限(run()除外,它必须设为public,因为它在基础类中是公开的)。除Counter2i之外,其他任何方面都不可访问private内部类。而且由于两个类紧密结合在一起,所以很容易放宽它们之间的访问限制。在SeparateSubTask中,我们可看到invertFlag()方法已被删去,因为Counter2i现在可以直接访问runFlag。
此外,注意SeparateSubTask的构建器已得到了简化——它现在唯一的用外就是启动线程。Counter2i对象的句柄仍象以前那样得以捕获,但不再是通过人工传递和引用外部对象来达到这一目的,此时的内部类机制可以自动照料它。在run()中,可看到对t的访问是直接进行的,似乎它是SeparateSubTask的一个字段。父类中的t字段现在可以变成private,因为SeparateSubTask能在未获任何特殊许可的前提下自由地访问它——而且无论如何都该尽可能地把字段变成“私有”属性,以防来自类外的某种力量不慎地改变它们。
无论在什么时候,只要注意到类相互之间结合得比较紧密,就可考虑利用内部类来改善代码的编写与维护。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索线程
, private
, new
, textfield 响应
, public
, 一个
void
pyqt4 多线程更新界面、mfc ui多线程更新界面、c 多线程 界面、qt多线程刷新界面、c 多线程操作界面,以便于您获取更多的相关知识。

时间: 2024-11-08 22:17:11

针对用户界面的多线程的相关文章

Linux的多线程编程的高效开发经验

简介:本文中我们针对 Linux 上多线程编程的主要特性总结出 5 条经验, 用以改善 Linux 多线程编程的习惯和避免其中的开发陷阱.在本文中,我们穿 插一些 Windows 的编程用例用以对比 Linux 特性,以加深读者印象. 背景 Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多线程 API 有一些细微和隐晦的差别.不注意这些 Linux 上的一些开发陷阱,常常会 导致程序问题不穷,死锁不断.本文中我们从 5 个方面总结出 Linux 多线程编 程上的问题,

Linux 的多线程编程的高效开发经验

  简介:          本文中我们针对 Linux 上多线程编程的主要特性总结出 5 条经验,用以改善 Linux 多线程编程的习惯和避免其中的开发陷阱.在本文中,我们穿插一些 Windows 的编程用例用以对比 Linux 特性,以加深读者印象.   背景:    Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多线程 API 有一些细微和隐晦的差别.不注意这些 Linux 上的一些开发陷阱,常常会导致程序问题不穷,死锁不断.本文中我们从 5 个方面总结出 Li

《Swift iOS应用开发实战》——2.3 创建用户界面

2.3 创建用户界面 此前我们在图2-3和图2-5中分别看到了一个空白视图和一个含有多个界面元素的视图.下面我们就通过动手实践来完成用户界面的搭建. 打开Calculator项目中的Main.storyboard文件,确保文档大纲可见,此时IB编辑器中只呈现一个View Controller视图控制器的View. 从Xcode 4.5开始,IB针对用户界面的布局加入了自动布局(Auto Layout)特性,并且该特性在Xcode 5中得到了很大的改进.如果说在Xcode 5中使用自动布局特性还是

Python 爬虫学习笔记之多线程爬虫_python

XPath 的安装以及使用 1 . XPath 的介绍 刚学过正则表达式,用的正顺手,现在就把正则表达式替换掉,使用 XPath,有人表示这太坑爹了,早知道刚上来就学习 XPath 多省事 啊.其实我个人认为学习一下正则表达式是大有益处的,之所以换成 XPath ,我个人认为是因为它定位更准确,使用更加便捷.可能有的人对 XPath 和正则表达式的区别不太清楚,举个例子来说吧,用正则表达式提取我们的内容,就好比说一个人想去天安门,地址的描述是左边有一个圆形建筑,右边是一个方形建筑,你去找吧,而使

.net开发中用BackgroundWorker实现多线程

背景介绍: 在做程序的过程中,我们很可能遇到这样的情况:当我们执行一个比较耗时的操作,即界面加载数据量略大的时,在该操作未完成之前再去操作界面,就会出现停止响应的情况,这称为界面假死状态,那一个小圆圈转呀转的,想必大家看着就头疼.当然这是一个非常影响用户体验度的地方. 怎么做出一个能够及时响应的用户界面呢?多线程操作. 引入BackgroundWorker组件: BackgroundWorker是·net里用来执行多线程任务的控件,它允许编程者在一个单独的线程上执行一些操作. 常用方法 1.Ru

垃圾收集器详解及参数配置

垃圾收集器详解及参数配置 垃圾搜集器简介 垃圾搜集器大致分为以下三类 串行搜集器(serial collector):它只有一条GC线程,且就像前面说的,它在运行的时候需要暂停用户程序(stop the world). 并行搜集器(parallel collector):它有多条GC线程,且它也需要暂停用户程序(stop the world). 并发搜集器(concurrent collector):它有一条或多条GC线程,且它需要在部分阶段暂停用户程序(stop the world),部分阶段

TIJ阅读笔记(第十三章)

笔记 13: 并发编程 面向对象使我们能将程序划分成相互独立的模块.但是你时常还会碰到,不但要把程序分解开来,而且还要让它的各个部分都能独立运行的问题. 这种能独立运行的子任务就是线程(thread).编程的时候,你可以认为线程都是能独立运行的,有自己CPU的子任务.实际上,是一些底层机制在为你分割CPU的时间,只是你不知道罢了.这种做法能简化多线程的编程. 进程(process)是一种有专属地址空间的"自含式(self-contained)"程序.通过在不同的任务之间定时切换CPU,

《OOD启思录》目录—导读

版权声明OOD启思录Authorized translation from the English language edition, entitled OBJECT-ORIENTED DESIGN HEURISTICS (PAPERBACK), 1E, 9780321774965 by RIEL, ARTHUR J., published by Pearson Education, Inc, publishing as Addison-Wesley Professional, Copyrigh

号外 UCenter Home1.5RC3 发布并提供下载

中介交易 SEO诊断淘宝客 站长团购 云主机 技术大厅 UCenter Home 1.5RC1.RC2发布以来,我们收到大量来自站长的建议和BUG反馈.并结合我们自己的大量调研工作,我们对UCenter Home进行了多达上百个的细节改进和完善. 终于在今天,UCenter Home 1.5RC3 正式公开发布并提供下载啦! UCenter Home 1.5RC3作为UCenter Home 1.5 正式版发布之前的最后一个版本,可能仍然存在一些小的BUG和待完善的地方,欢迎各位站长下载安装.或