1. 线程和多线程
1.1 线程的概念
提到线程,首先要从 “进程” 开始讲起。对于一般程序而言,其结构大部分都可以划分为一个入口、一个出口和一个顺次执行的语句序列。在程序投入运行时,系统从程序入口开始按语句的顺序(其中包括顺序、分支和循环)完成相应指令直至结尾,从出口退出,同时整个程序结束。这样的语句结构称为进程,它是程序的一次动态执行,对应了从代码加载、执行至执行完毕的一个完整过程;或者说,进程就是程序在处理机中的一次运行。需要特别明确的是,在这样的一个结构中,不仅包括了程序代码,同时也包括了系统资源的概念。也就是说,一个进程既包括其所要执行的指令,也包括了执行指令所需的任何系统资源,如CPU、内存空间、I/O端口等,不同进程所占用的系统资源相对独立。
线程 是进程执行过程中产生的多条执行线索,是比进程单位更小的执行单位,在形式上同进程十分相似————都是用一个顺序执行的语句序列来完成特定的功能。不同的是,它没有入口,也没有出口,因此其自身不能自动运行,而必须栖身于某一进程之中,由进程触发执行。而且在系统资源的使用上,属于同一进程的所有线程共享该进程的系统资源,但是线程之间切换的速度比进程切换要快得多。
Java 类库中的类 java.lang.Thread 允许创建这样的线程,并可控制所创建的线程。
1.2 线程的结构
在 Java 中,线程可以认为是由三部分组成的:
-
虚拟 CPU,封装在 java.lang.Thread 类中,它控制着整个线程的运行。
-
执行的代码,传递给 Thread 类,由 Thread 类控制顺序执行。
-
处理的数据,传递给 Thread 类,是在代码执行过程中所要处理的数据。
在 Java 中,虚拟 CPU 体现于 Thread 类中。当一个线程被构造时,它由构造方法参数、执行代码、操作数据来初始化。应该特别注意的时,这三方面是各自独立的。一个线程所执行的代码与其他线程可以相同,也可以不同;一个线程访问的数据与其他线程可以相同,也可以不同。
多线程的优势体现在以下几个方面:
-
多线程编程简单,效率高。使用多线程可以在线程间直接共享数据和资源,而多线程之间不能做到这一点。
-
适合于开发服务程序,如 Web 服务、聊天服务等。
-
适合于开发有多种交互接口的程序,如聊天程序的客户端、网络下载工具。
-
适合于有人机交互又有计算量的程序,如字处理程序Word、Excel等。
2. 线程的状态
Java 的线程是通过 Java 软件包 java.lang 中定义的类 Thread 来实现的。当生成一个 Thread 类的对象之后,是产生了一个线程。通过该对象实例,可以启动线程、终止线程,或者暂时挂起线程等。
Thread 类本身只是线程的虚拟 CPU,线程所执行的代码(或者说线程所要完成的工作)是通过方法 run()(包含在一个特定的对象中)完成的,方法 run() 称为线程体。实现线程体的特定对象是在初始化线程时传递给线程的。
在一个线程被建立并完成初始化以后,Java 的运行时系统自动调用 run() 方法,正是通过 run() 方法才使得建立线程的目的得以实现。
线程一共有 4 中状态:新建(new)、可运行状态(runnable)、死亡(dead)及阻塞(blocked),如图所示:
(1)新建
线程对象刚刚创建,还没有启动,处于不可运行状态,如:
Thread thread = new Thread("Test");
此时线程 thread 处于新建状态,但已有了相应的内存空间以及其他资源。
(2)可运行状态(runnable)
此时的线程已经启动,处于线程的 run() 方法之中。这种情况下线程可能正在运行,也可能没有运行,只要 CPU 一空闲,马上就会运行。可以运行但并没在运行的线程都排在一个队列之中,这个队列称为就绪队列。
调用线程 start() 方法可使线程处于 “可运行” 状态,如:
thread.start();
(3)死亡(dead)
线程死亡的原因有两个:一是 run() 方法中最后一个语句执行完毕;二是当线程遇到异常退出时便进入了死亡状态。
(4)阻塞(blocked)
一个正在执行的线程因特殊原因被暂停执行,就进入阻塞状态。阻塞时线程不能进入就绪队列排队,必须等到引起阻塞的原因消除,才可重新进入队列排队。
引起阻塞的原因有很多,不同原因要用不同的方法解除。如 sleep() 和 wait() 就是两个常用的引起阻塞的方法。
(5)中断线程
当 run() 执行结束返回时,线程自动终止。
在程序中常常调用 interrupt() 来终止线程。interrupt() 不仅可中断正在运行的线程,而且也能中断处于 blocked 状态的线程,此时 interrupt() 会抛出一个 InterruptedException 异常。
Java 提供了几个用于测试线程是否被中断的方法:
-
void interrupt() ————向一个线程发送一个中断请求,同时把这个线程的 “interrupted” 状态置为 true。若该线程处于 “blocked” 状态,会抛出一个 InterruptedException 异常。
-
static boolean interrupted() ————检测当前线程是否已被中断,并重置状态 “interrupted” 值。即如果连续两次调用该方法,则第二次调用将返回 false。
-
boolean isInterrupted ————检测当前线程是否已被中断,不改变状态 “interrupted” 值。