线程与线程类
1 线程的概念
线程的概念来源于计算机的操作系统的进程的概念。进程是一个程序关于某个数据集的一次运行。也就是说,进程是运行中的程序,是程序的一次运行活动。
线程和进程的相似之处在于,线程和运行的程序都是单个顺序控制流。有些教材将线程称为轻量级进程(light weight process)。线程被看作是轻量级进程是因为它运行在一个程序的上下文内,并利用分配给程序的资源和环境。
作为单个顺序控制流,线程必须在运行的程序中得到自己运行的资源,如必须有自己的执行栈和程序计数器。线程内运行的代码只能在该上下文内。因此还有些教程将执行上下文(execution context)作为线程的同义词。
所有的程序员都熟悉顺序程序的编写,如我们编写的名称排序和求素数的程序就是顺序程序。顺序程序都有开始、执行序列和结束,在程序执行的任何时刻,只有一个执行点。线程(thread)则是进程中的一个单个的顺序控制流。单线程的概念很简单,如图1所示。
多线程(multi-thread)是指在单个的程序内可以同时运行多个不同的线程完成不同的任务,图2说明了一个程序中同时有两个线程运行。
一个线程 两个线程 图1 单线程程序示意图 图2 多线程程序示意图
有些程序中需要多个控制流并行执行。例如,
for(int i = 0; i < 100; i++)
System.out.println(\for(int j = 0; j < 100; j++ )
System.out.println(\上面的代码段中,在只支持单线程的语言中,前一个循环不执行完不可能执行第二个循环。要使两个循环同时执行,需要编写多线程的程序。
很多应用程序是用多线程实现的,如Hot Java Web浏览器就是多线程应用的例子。在Hot Java 浏览器中,你可以一边滚动屏幕,一边下载Applet或图像,可以同时播放动画和声音等。
2 Thread类和Runnable接口
多线程是一个程序中可以有多段代码同时运行,那么这些代码写在哪里,如何创建线程对象呢?
首先,我们来看Java语言实现多线程编程的类和接口。在java.lang包中定义了Runnable接口和Thread类。
Runnable接口中只定义了一个方法,它的格式为: ? public abstract void run()
这个方法要由实现了Runnable接口的类实现。Runnable对象称为可运行对象,一个线程的运行就是执行该对象的run()方法。
Thread类实现了Runnable接口,因此Thread对象也是可运行对象。同时Thread类也是线程类,该类的构造方法如下:
? public Thread()
? public Thread(Runnable target) ? public Thread(String name)
? public Thread(Runnable target, String name)
? public Thread(ThreadGroup group, Runnable target) ? public Thread(ThreadGroup group, String name)
? public Thread(ThreadGroup group, Runnable target, String name) target为线程运行的目标对象,即线程调用start()方法启动后运行那个对象的run()方法,该对象的类型为Runnable,若没有指定目标对象,则以当前类对象为目标对象;name为线程名,group指定线程属于哪个线程组(有关线程组的概念请参考9.6节)。
Thread类的常用方法有:
? public static Thread currentThread() 返回当前正在执行的线程对象的引用。 ? public void setName(String name) 设置线程名。 ? public String getName() 返回线程名。
? public static void sleep(long millis) throws InterruptedException
? public static void sleep(long millis, int nanos) throws InterruptedException
使当前正在执行的线程暂时停止执行指定的毫秒时间。指定时间过后,线程继续执行。该方法抛出InterruptedException异常,必须捕获。 ? public void run() 线程的线程体。
? public void start() 由JVM调用线程的run()方法,启动线程开始执行。 ? public void setDaemon(boolean on) 设置线程为Daemon线程。 ? public boolean isDaemon() 返回线程是否为Daemon线程。
? public static void yield() 使当前执行的线程暂停执行,允许其他线程执行。 ? public ThreadGroup getThreadGroup() 返回该线程所属的线程组对象。 ? public void interrupt() 中断当前线程。
? public boolean isAlive() 返回指定线程是否处于活动状态。
线程的创建
本节介绍如何创建和运行线程的两种方法。线程运行的代码就是实现了Runnable接口的类的run()方法或者是Thread类的子类的run()方法,因此构造线程体就有两种方法:
? 继承Thread类并覆盖它的run()方法;
? 实现Runnable接口并实现它的run()方法。 继承Thread类创建线程
通过继承Thread类,并覆盖run()方法,这时就可以用该类的实例作为线程的目标对象。下面的程序定义了SimpleThread类,它继承了Thread类并覆盖了run()方法。
程序SimpleThread.java
public class SimpleThread extends Thread{ public SimpleThread(String str){ super(str); }
public void run(){
for(int i=0; i<100; i++){
System.out.println(getName()+\ try{
sleep((int)(Math.random()*100)); }catch(InterruptedException e){} }
System.out.println(getName()+ \} }
_____________________________________________________________________________▃ SimpleThread类继承了Thread类,并覆盖了run()方法,该方法就是线程体。
程序 ThreadTest.java public class ThreadTest{
public static void main(String args[]){
Thread t1 = new SimpleThread(\ Thread t2 = new SimpleThread(\ t1.start(); t2.start(); } }
_____________________________________________________________________________▃ 在ThreadTest类的main()方法中创建了两个SimpleThread类的线程对象并调用线程类的start()方法启动线程。构造线程时没有指定目标对象,所以线程启动后执行本类的run()方法。
注意,实际上ThreadTest程序中有三个线程同时运行。请试着将下段代码加到main()方法中,分析程序运行结果。
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName()+\ try{
Thread.sleep((int)(Math.random()*500)); }catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+ \ }
从上述代码执行结果可以看到,在应用程序的main()方法启动时,JVM就创建一个主线程,在主线程中可以创建其他线程。
再看下面的程序:
程序 MainThreadDemo.java
public class MainThreadDemo{
public static void main(String args[]){ Thread t = Thread.currentThread(); t.setName(\ System.out.println(t);
System.out.println(t.getName());
System.out.println(t.getThreadGroup().getName()); } }
_____________________________________________________________________________▃ 该程序输出结果为:
Thread[MyThread, 5, main] MyThread main
上述程序在main()方法中声明了一个Thread对象t,然后调用Thread类的静态方法currentThread()获得当前线程对象。然后重新设置该线程对象的名称,最后输出线程对象、线程组对象名和线程对象名。 实现Runnable接口创建线程
可以定义一个类实现Runnable接口,然后将该类对象作为线程的目标对象。实现Runnable接口就是实现run()方法。
下面程序通过实现Runnable接口构造线程体。 程序 ThreadTest.java
class T1 implements Runnable{ public void run(){
for(int i=0;i<15;i++)
System.out.println(\ } }
class T2 implements Runnable{ public void run(){
for(int j=0;j<15;j++)
System.out.println(\ } }
public class ThreadTest{
public static void main(String args[]){
Thread t1=new Thread(new T1(),\ Thread t2=new Thread(new T2(),\ t1.start(); t2.start(); } }
_____________________________________________________________________________▃
线程的状态与调度
1线程的生命周期
线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。线程的状态如图4所示:
新建状态 就绪状态 阻塞状态 运行状态 死亡状态 图4 线程的五种状态
下面以前面的Java小程序为例说明线程的状态: