当前位置: 首页 > 图文教程 > Java技术 > Java基础 > Java基础:浅谈多线程那点事

Java基础
Java串行端口技术协议
Java 1.5中面向方面(AOP)编程
实现基于Aspectwerkz的AOP开发
EJB 3.0开发指南之消息驱动Bean
Java堆的管理--垃圾回收
EJB 3.0开发指南之依赖注入
EJB3.0开发指南之有状态会话Bean
EJB3.0开发指南之无状态会话Bean
基于Java的企业分布式应用
J2SE5.0新特性之ProcessBuilder
J2SE5.0新特性之使用代理服务器
Java数据对象技术JDO
Java中ThreadLocal的设计与使用
Hibernate下数据批量处理
Java程序设计中的接口应用
Java中利用JMF编写摄像头拍照程序
性能、规模、风险 初评hibernate
Struts+Hibernate中解决汉字编码
Spring Framework标记库初学指南
基于Java的IDEA加密算法

Java基础:浅谈多线程那点事


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-08-14   浏览: 108 ::
收藏到网摘: n/a

    在J2SE中线程的使用很频繁,其实主要作用就是使程序灵敏些,今天小编整理了一下资料,希望能对大家有所帮助。

利用对象,可将一个程序分割成相互独立的区域。我们通常也需要将一个程序转换成多个独立运行的子任务。

象这样的每个子任务都叫作一个“线程”(Thread)。编写程序时,可将每个线程都想象成独立运行,而且都有自己的专用CPU。一些基础机制实际会为我们自动分割CPU的时间。我们通常不必关心这些细节问题,所以多线程的代码编写是相当简便的。

这时理解一些定义对以后的学习狠有帮助。“进程”是指一种“自包容”的运行程序,有自己的地址空间。“多任务”操作系统能同时运行多个进程(程序)——但实际是由于CPU分时机制的作用,使每个进程都能循环获得自己的CPU时间片。但由于轮换速度非常快,使得所有程序好象是在“同时”运行一样。“线程”是进程内部单一的一个顺序控制流。因此,一个进程可能容纳了多个同时执行的线程。

多线程的应用范围很广。但在一般情况下,程序的一些部分同特定的事件或资源联系在一起,同时又不想为它而暂停程序其他部分的执行。这样一来,就可考虑创建一个线程,令其与那个事件或资源关联到一起,并让它独立于主程序运行。一个很好的例子便是“Quit”或“退出”按钮——我们并不希望在程序的每一部分代码中都轮询这个按钮,同时又希望该按钮能及时地作出响应(使程序看起来似乎经常都在轮询它)。事实上,多线程最主要的一个用途就是构建一个“反应灵敏”的用户界面。

作为我们的起点,请思考一个需要执行某些CPU密集型计算的程序。由于CPU“全心全意”为那些计算服务,所以对用户的输入十分迟钝,几乎没有什么反应。在这里,我们用一个合成的applet/application(程序片/应用程序)来简单显示出一个计数器的结果:

 

 

//: Counter1.java

// A non-responsive user interface

package c14;

import java.awt.*;

import java.awt.event.*;

import java.applet.*;

 

public class Counter1 extends Applet {

  private int count = 0;

  private Button

    onOff = new Button("Toggle"),

    start = new Button("Start");

  private TextField t = new TextField(10);

  private boolean runFlag = true;

  public void init() {

    add(t);

    start.addActionListener(new StartL());

    add(start);

    onOff.addActionListener(new OnOffL());

    add(onOff);

  }

  public void go() {

    while (true) {

      try {

        Thread.currentThread().sleep(100);

      } catch (InterruptedException e){}

      if(runFlag)

        t.setText(Integer.toString(count++));

    }

  }

  class StartL implements ActionListener {

    public void actionPerformed(ActionEvent e) {

      go();

    }

  }

  class OnOffL implements ActionListener {

    public void actionPerformed(ActionEvent e) {

      runFlag = !runFlag;

    }

  }

  public static void main(String[] args) {

    Counter1 applet = new Counter1();

    Frame aFrame = new Frame("Counter1");

    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);

  }

} ///:~

 

在这个程序中,AWT和程序片代码都应是大家熟悉的,go()方法正是程序全心全意服务的对待:将当前的count(计数)值置入TextField(文本字段)t,然后使count增值。

go()内的部分无限循环是调用sleep()sleep()必须同一个Thread(线程)对象关联到一起,而且似乎每个应用程序都有部分线程同它关联(事实上,Java本身就是建立在线程基础上的,肯定有一些线程会伴随我们写的应用一起运行)。所以无论我们是否明确使用了线程,都可利用Thread.currentThread()产生由程序使用的当前线程,然后为那个线程调用sleep()。注意,Thread.currentThread()Thread类的一个静态方法。

注意sleep()可能“掷”出一个InterruptException(中断违例)——尽管产生这样的违例被认为是中止线程的一种“恶意”手段,而且应该尽可能地杜绝这一做法。再次提醒大家,违例是为异常情况而产生的,而不是为了正常的控制流。在这里包含了对一个“睡眠”线程的中断,以支持未来的一种语言特性。

一旦按下start按钮,就会调用go()。研究一下go(),你可能会很自然地(就象我一样)认为它该支持多线程,因为它会进入“睡眠”状态。也就是说,尽管方法本身“睡着”了,CPU仍然应该忙于监视其他按钮“按下”事件。但有一个问题,那就是go()是永远不会返回的,因为它被设计成一个无限循环。这意味着actionPerformed()根本不会返回。由于在第一个按键以后便陷入actionPerformed()中,所以程序不能再对其他任何事件进行控制(如果想出来,必须以某种方式“杀死”进程——最简便的方式就是在控制台窗口按CtrlC键)。

线程机制多少降低了一些计算效率,但无论程序的设计,资源的均衡,还是用户操作的方便性,都从中获得了巨大的利益。综合考虑,这一机制是非常有价值的。当然,如果本来就安装了多块CPU,那么操作系统能够自行决定为不同的CPU分配哪些线程,程序的总体运行速度也会变得更快(所有这些都要求操作系统以及应用程序的支持)。多线程和多任务是充分发挥多处理机系统能力的一种最有效的方式。