一般来说我们把正在计算机中执行的程序叫做进程(Process) 而不将其称为程序(Program)所谓线程(Thread)是进程中某个单一顺序的控制流新兴的操作系统如MacWindows NTWindows 等大多采用多线程的概念把线 程视为基本执行单位线程也是Java中的相当重要的组成部分之一
甚至最简单的Applet也是由多个线程来完成的在Java中任何一个Applet的paint()和update()方法都是由AWT(Abstract Window Toolkit)绘图与事件处理线程调用的而Applet 主要的里程碑方法——init()start()stop()和destory() ——是由执行该Applet的应用调用的
单线程的概念没有什么新的地方真正有趣的是在一个程序中同时使用多个线程来完成不同的任务某些地方用轻量进程(Lightweig ht Process)来代替线程线程与真正进程的相似性在于它们都是单一顺序控制流然而线程被认为轻量是由于它运行于整个程序的上下文内能使用整个程序共有的资源和程序环境
作为单一顺序控制流在运行的程序内线程必须拥有一些资源作为必要的开销例如必须有执行堆栈和程序计数器在线程内执行的代码只在它的上下文中起作用因此某些地方用执行上下文来代替线程
线程属性
为了正确有效地使用线程必须理解线程的各个方面并了解Java 实时系统必须知道如何提供线程体线程的生命周期实时系统如何调度线程线程组什么是幽灵线程(Demo nThread)
()线程体
所有的操作都发生在线程体中在Java中线程体是从Thread类继承的run()方法或实现Runnable接口的类中的run()方法当线程产生并初始化后实时系统调用它的run()方法run()方法内的代码实现所产生线程的行为它是线程的主要部
分
()线程状态
附图表示了线程在它的生命周期内的任何时刻所能处的状态以及引起状态改变的方法这图并不是完整的有限状态图但基本概括了线程中比较感兴趣和普遍的方面以下讨论有关线程生命周期以此为据
●新线程态(New Thread)
产生一个Thread对象就生成一个新线程当线程处于新线程状态时仅仅是一个空线程对象它还没有分配到系统资源因此只能启动或终止它任何其他操作都会引发异常
●可运行态(Runnable)
start()方法产生运行线程所必须的资源调度线程执行并且调用线程的run ()方法在这时线程处于可运行态该状态不称为运行态是因为这时的线程并不总是一直占用处理机特别是对于只有一个处理机的PC而言任何时刻只能有一个处于可运行态的线程占用处理 机Java通过调度来实现多线程对处理机的共享
●非运行态(Not Runnable)
当以下事件发生时线程进入非运行态
①suspend()方法被调用;
②sleep()方法被调用;
③线程使用wait()来等待条件变量;
④线程处于I/O等待
●死亡态(Dead)
当run()方法返回或别的线程调用stop()方法线程进入死亡态 通常Applet使用它的stop()方法来终止它产生的所有线程
()线程优先级
虽然我们说线程是并发运行的然而事实常常并非如此正如前面谈到的当系统中只有一个CPU时以某种顺序在单CPU情况下执行多线程被称为调度(scheduling)Java采用的是一种简单固定的调度法即固定优先级调度这种算法是根据处于可运行态线程的相对优先级来实行调度当线程产生时它继承原线程的优先级在需要时可对优先级进行修改在任何时刻如果有多条线程等待运行 系统选择优先级最高的可运行线程运行只有当它停止自动放弃或由于某种原因成为非运行态低优先级的线程才能运行如果两个线程具有相同的优先级它们将被交替地运行
Java实时系统的线程调度算法还是强制性的在任何时刻如果一个比其他线程优先级都高的线程的状态变为可运行态实时系统将选择该线程来运行
()幽灵线程
任何一个Java线程都能成为幽灵线程它是作为运行于同一个进程内的对象和线程的服务提供者例如HotJava浏览器有一个称为 后台图片阅读器的幽灵线程它为需要图片的对象和线程从文件系统或网络读入图片幽灵线程是应用中典型的独立线程它为同一应用中的其他对象和线程提供服务幽灵线程的run()方法一般都是无限循环等待服务请求
()线程组
每个Java线程都是某个线程组的成员线程组提供一种机制使得多个线程集于一个对象内能对它们实行整体操作譬如你能用一个方法调用来启动或挂起组内的所有线程Java线程组由ThreadGroup类实现当线程产生时可以指定线程组或由实时系统将其放入某个缺省的线程组内线程只能属于一个线程组并且当线程产生后不能改变它所属的线程组
多线程程序
对于多线程的好处这就不多说了但是它同样也带来了某些新的麻烦只要在设计程序时特别小心留意克服这些麻烦并不算太困难
()同步线程
许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态这就 需要同步机制在Java中每个对象都有一把锁与之对应但Java不提供单独的lock和unlock操作它由高层的结构隐式实现 来保证操作的对应(然而我们注意到Java虚拟机提供单独的monito renter和monitorexit指令来实现lock和unlock操作)
synchronized语句计算一个对象引用试图对该对象完成锁操作 并且在完成锁操作前停止处理当锁操作完成synchronized语句体得到执行当语句体执行完毕(无论正常或异常)解锁操作自动完成作为面向对象的语言synchronized经常与方法连用一种比较好的办法是如果某个变量由一个线程赋值并由别的线程引用或赋值那么所有对该变量的访问都必须在某个synchromized语句或synchronized方法内
现在假设一种情况线程与线程都要访问某个数据区并且要求线程的访问先于线程 则这时仅用synchronized是不能解决问题的这在Unix或Windows NT中可用Simaphore来实现而Java并不提供在Java中提供的是wait()和notify()机制使用如下:
synchronized method(…){ call by thread
∥access data area;
available=true;
notify()
}
synchronized method(…){∥call by thread
while(!available)
try{
wait();∥wait for notify()
}catch (Interrupted Exception e){
}
∥access data area
}
其中available是类成员变量置初值为false如果在method中检查available为假则调用wait()wait()的作用是使线程进入非运行态并且解锁在这种情况下method可以被线程调用当执行 notify()后线程由非运行态转变为可运行态当method调用返回后线程 可重新对该对象加锁加锁成功后执行wait()返回后的指令这种机制也能适用于 其他更复杂的情况
()死锁
如果程序中有几个竞争资源的并发线程那么保证均衡是很重要的系统均衡是指每个线程在执行过程中都能充分访问有限的资源系统中没有饿死和死锁的线程Java并不提供对死锁的检测机制对大多数的Java程序员来说防止死锁是一种较好的选择最简单的防止死锁的方法是对竞争的资源引入序号如果一个线程需要几个资源那么它必须先得到小序号的资源再申请大序号的资源
小结
线程是Java中的重要内容多线程是Java的一个特点虽然Java的同步互斥不如某些系统那么丰富但适当地使用它们也能收到满意的效果