1、本章学习目标:掌握线程创建的过程 掌握线程的生命周期 掌了解线程同步机制以及线程通信 了解线程的优先级 掌握线程的同步与死锁第第十二十二章章 多线程多线程第第1 1节节part线程概述 线程(Thread)在多任务处理应用程序中起着至关重要的作用。之前所接触的应用程序都是采用单线程处理模式。单线程在某些功能方面会受到限制,无法同时处理多个互不干扰的任务,只有一个顺序执行流;而多线程是同时有多个线程并发执行,同时完成多个任务,具有多个顺序执行流,且执行流之间互不干扰。Java语言多多线程提供了非常优秀的支持,在程序中可以通过简便的方式创建多线程。线程概述本节概述 在操作系统中,每个独立运行的程序
2、就是一个进程(Process),当一个程序进入内存运行时,即变成一个进程。进程是操作系统进行资源分配和调度的一个独立单位,是具有独立功能且处于运行过程中的程序。在Windows操作系统中,右击任务栏,选择“启动任务管理器”菜单命令,可以打开“Windows任务管理器”窗口,该窗口中的“进程”选项卡中显示系统当前正在运行的进程,如图12.1所示。12.1.1线程和进程线程和进程 进程具有如下三个特征:(1)独立性:进程是操作系统中独立存在的实体,拥有自己独立的资源,每个进行都拥有自己私有的地址空间,其他进程不可以直接访问该地址空间,除非进程本身允许的情况下才能进行访问。(2)动态性:程序只是一个
3、静态的指令集合,只有当程序进入内存运行时,才变成一个进程。进程是一个正在内存中运行的、动态的指令集合,进程具有自己的生命周期和各种不同状态。(3)并发性:多个进程可以在单个处理器上并发执行,多个进程之间互不影响。12.1.1线程和进程 目前的操作系统都支持多线程的并发,但在具体的实现细节上会采用不同的策略。对于一个CPU而言,在某一时间点只能执行一个进程,CPU会不断在多个进程之间来回轮换执行。并发性(concurrency)和并行性(parallel)是两个相似但又不同的概念:并发是指多个事件在同一时间间隔内发生,其实质是在一个CPU上同时运行多个进程,CPU要在多个进程之间切换。并发不是真
4、正的同时发生,而是对有限物理资源进行共享以便提高效率。并行是指多个事件在同一时刻发生,其实质是多个进程同一时刻可在不同的CPU上同时执行,每个CPU运行一个进程。12.1.1线程和进程 并发就像一个人喂两个孩子吃饭,轮换着每人喂一口,表面上两个孩子都在吃饭;而并行就是两个人喂两个孩子吃饭,两个孩子也同时在吃饭。并发和并行之间的区别如图12.2所示。12.1.1线程和进程 线程是进程的组成部分,一个线程必须在一个进程之内,而一个进程可以拥有多个线程,一个进程中至少有一个线程。线程是最小的处理单位,线程可以拥有自己的堆栈、计数器和局部变量,当不能拥有系统资源,多个线程共享其所在进程的系统资源。线程
5、可以完成一定的任务,使用多线程可以在一个程序中同时完成多个任务,在更低的层次中引入多任务处理。多线程在多CPU的计算机中可以实现真正物理上的同时执行;而对于单CPU的计算机实现的只是逻辑上的同时执行,在每个时刻,真正执行的只有一个线程,由操作系统进行线程管理调度,但由于CPU的速度很快,让人感到像是多个线程在同时执行。12.1.1线程和进程 多线程扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。因此,线程也被称作轻量级进程。多进程与多线程是多任务的两种类型,两者之间的主要区别如下:(1)进程之间的数据块是相互独立的,彼此互不影响,进程之间需要通过信号、管道等进行交互。(2)多线程之
6、间的数据块可以共享,一个进程中的多个线程可以共享程序段、数据段等资源。多线程比多进程更便于资源共享,同时Java提供的同步机制还可以解决线程之间的数据完整性问题,使得多线程设计更易发挥作用。多线程编程的优点如下:(1)多线程之间共享内存,节约系统资源成本;(2)充分利用CPU,执行并发任务效率高;(3)Java内置多线程功能支持,简化编程模型 (4)GUI应用通过启动单独线程收集用户界面事件,简化异步事件处理,使GUI界面的交互性更好。12.1.1线程和进程 Java线程模型提供线程所必需的功能支持,基本的Java线程模型有Thread类、Runnable接口、Callable接口和Futur
7、e接口等,这些线程模型都是面向对象的。Thread类将线程所必需的功能进行封装,其常用的方法如表12-1所示。12.1.2Java线程模型Java线程模型 Thread类的run()方法是线程中最重要的方法,该方法用于执行线程要完成的任务;当创建一个线程时,要完成自己的任务,则需要重写run()方法。此外,Thread类还提供了start()方法,该方法用于负责线程的启动;当调用start()方法成功地启动线程后,系统会自动调用Thread类的run()方法来执行线程。因此,任何继承Thread类的线程都可以通过start()方法来启动。Runnable接口用于标识某个Java类可否作为线程类
8、,该接口只有一个抽象方法run(),即线程中最重要的执行体,用于执行线程中所要完成的任务。Runnable接口定义在java.lang包中,定义代码如下所示。package java.lang;public interface Runnable public abstract void run();12.1.2Java线程模型 Callable接口是Java5 新增的接口,该接口中提供一个call()方法作为线程的执行体。call()方法比run()方法功能更强大,call()方法可以有返回值,也可以声明抛出异常。Callable接口定义在java.util.concurrent包中,定义代码
9、如下所示。package java.util.concurrent;public interface Callable V call()throws Exception;12.1.2Java线程模型 Future接口用来接收Callable接口中call()方法的返回值。Future接口提供一些方法用于控制与其关联的Callable任务。Future接口提供的方法如表12-2所示。12.1.2Java线程模型 Callable接口有泛型限制,该接口中的泛型形参类型与call()方法返回值的类型相同;而且Callable接口是函数式接口,因此从Java 8开始可以使用Lambda表达式创建Cal
10、lable对象。每个能够独立运行的程序就是一个进程,每个进程至少包含一个线程,即主线程。在Java语言中,每个能够独立运行的Java程序都至少有一个主线程,且在程序启动时,JVM会自动创建一个主线程来执行该程序中的main()方法。因此,主线程有以下两个特点:(1)一个进程肯定包含一个主线程 (2)主线程用来执行main()方法 下述程序在main()方法中,调用Thread类的静态方法currentThread()来获取主线程,代码如下所示。12.1.3主线程主线程【代码12.1】MainThreadExample.javapackage com;public class MainThrea
11、dExample public static void main(String args)/调用Thread类的currentThread()获取当前线程 Thread t=Thread.currentThread();/设置线程名 t.setName(My Thread);System.out.println(主线程是:+t);System.out.println(线程名:+t.getName();System.out.println(线程ID:+t.getId();12.1.3主线程 上述代码中,通过Thread.currentThread()静态方法来获取当前线程对象,由于是在main(
12、)方法中,所以获取的线程是主线程。调用setName()方法可以设置线程名,调用getId()方法可以获取线程的Id号,调用getName()方法可以获取线程的名字。程序运行结果如下:主线程是:ThreadMy Thread,5,main 线程名:My Thread 线程ID:112.1.3主线程第第2 2节节part线程的创建和启动 基于Java线程模型,创建线程的方式有三种:(1)第一种方式是继承Thread类,重写Thread类中的run()方法,直接创建线程。(2)第二种方式是实现Runnable接口,再通过Thread类和Runnable的实现类间接创建一个线程。(3)第三种方式是使
13、用Callable接口或Future接口间接创建线程。上述三种方式从本质上是一致的,最终都是通过Thread类来建立线程。提供Runnable、Callable和Future接口模型是由于Java不支持多继承,如果一个线程类继承了Thread类,则不能再继承其他的类,因此可以通过实现接口的方式间接创建线程。采用Runnable、Callable和Future接口的方式创建线程时,线程类还可以继承其他类,且多个线程之间可以共享一个target目标对象,适合多个相同线程处理同一个资源的情况,从而可以将CPU、代码和数据分开,形成清晰的数据模型。线程的启动和创建本节概述12.2.1继承Thread类
14、 通过继承Thread类来创建并启动线程的步骤如下:(1)定义一个子类继承Thread类,并重写run()方法。(2)创建子类的实例,即实例化线程对象。(3)调用线程对象的start()方法启动该线程。Thread类的start()方法将调用run()方法,该方法用于启动线程并运行。因此start()方法不能多次调用,当多次调用td.start()方法时会抛出一个IllegalThreadStateException异常。下述案例示例通过继承Thread类来创建并启动线程的步骤,代码如下所示。继承Thread类继承Thread类【代码12.2】ThreadExample.javapackage
15、 com;/继承Thread类public class ThreadExample extends Thread/重写run()方法public void run()for(int i=0;i 10;i+)/继承Thread类时,直接使用this即可获取当前线程对象/调用getName()方法返回当前线程的名字System.out.println(this.getName()+:+i);12.2.1继承Thread类public static void main(String args)/创建线程对象ThreadExample td=new ThreadExample();/调用start()
16、方法启动线程td.start();/主线程任务for(int i=1100;i 1110;i+)/使用Thread.currentThread().getName()获取主线程名字System.out.println(Thread.currentThread().getName()+:+i);12.2.1继承Thread类 因为线程在CPU中的执行是由操作系统所控制,执行次序是不确定的,除非使用同步机制强制按特定的顺序执行,所以程序代码运行的结果会因调度次序不同而不同。程序执行结果可能如下:main:1101 Thread-0:1 main:1102 Thread-0:2 main:1103
17、.在创建td线程对象时并未指定该线程的名字,因此所输出的线程名是系统的默认值“Thread-0”。对于输出结果,不同机器所执行的结果可能不同,在同一机器上多次运行同一个程序也可能生成不同结果。12.2.112.2.2实现Runable接口 创建线程的第二种方式是实现Runnable接口。Runnable接口中只有一个run()方法,一个类实现Runnable接口后,并不代表该类是个“线程”类,不能直接启动线程,必须通过Thread类的实例来创建并启动线程。通过Runnable接口创建并启动线程的步骤如下:(1)定义一个类实现Runnable接口,并实现该接口中的run()方法;(2)创建一个T
18、hread类的实例,将Runnable接口的实现类所创建的对象作为参数传入Thread类的构造方法中;(3)调用Thread对象的start()方法启动该线程。下述案例示例通过实现Runnable接口创建并启动线程的步骤,代码如下所示。实现Runable接口实现Runable接口【代码12.3】RunnableExamble.javapackage com;/实现Runnable接口public class RunnableExamble implements Runnable/重写run()方法public void run()/获取当前线程的名字for(int i=0;i 10;i+)/实
19、现Runnable接口时,只能使用Thread.currentThread()获取当前线程对象/再调用getName()方法返回当前线程的名字System.out.println(Thread.currentThread().getName()+:+i);12.2.2实现Runable接口public static void main(String args)/创建一个Thread类的实例,其参数是RunnableExamble类的对象Thread td=new Thread(new RunnableExamble();/调用start()方法启动线程td.start();/主线程任务for(
20、int i=1100;i 1110;i+)/使用Thread.currentThread().getName()获取主线程名字System.out.println(Thread.currentThread().getName()+:+i);12.2.2实现Runable接口 上述代码定义了一个RunnableExamble类,该类实现了Runnable接口,并实现run()方法,这样的类可以称为线程任务类。直接调用Thread类或Runnable接口所创建的对象的run()方法是无法启动线程的,必须通过Thread的start()方法才能启动线程。程序执行的结果可能如下:main:1100 T
21、hread-0:0 Thread-0:1 Thread-0:2.12.2.212.2.3使用Callable和Future接口 创建线程的第三种方式是使用Callable和Future接口。Callable接口提供一个call()方法作为线程的执行体,该方法的返回值使用Future接口来代表。从Java 5开始,为Future接口提供一个FutureTask实现类,该类同时实现了Future和Runnable两个接口,因此可以作为Thread类的target参数。使用Callable和Future接口的最大优势在于可以在线程执行完成之后获得执行结果。使用Callable和Future接口创建并
22、启动线程的步骤如下:(1)创建Callable接口的实现类,并实现call()方法,该方法将作为线程的执行体,并具有返回值;然后创建Callable实现类的实例。(2)使用FutureTask类来包装Callable对象,在FutureTask对象中封装了Callable对象的call()方法的返回值。(3)使用FutureTask对象作为Thread对象的target,创建并启动新线程。(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。下述案例示例通过Callable和Future接口创建并启动线程的步骤,代码如下所示。使用Callable和Future接口使
23、用Callable和Future接口【代码12.4】CallableFutureExample.javapackage com;import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;/创建Callable接口的实现类class Task implements Callable/实现call()方法,作为线程执行体public Integer call()throws Exception int i=0;for(;i 10;i+)System.out.println(Thread.currentTh
24、read().getName()+:+i);/call()方法可以有返回值return i;12.2.3使用Callable和Future接口public class CallableFutureExample public static void main(String args)/使用FutureTask类包装Callable实现类的实例FutureTask task=new FutureTask(new Task();/创建线程,使用FutureTask对象task作为Thread对象的target,/并调用start()方法启动线程Thread td=new Thread(task,子
25、线程);td.start();/调用FutureTask对象task的get()方法获取子线程执行结束后的返回值try System.out.println(子线程返回值:+task.get();catch(Exception e)e.printStackTrace();/主线程任务for(int i=1100;i 1110;i+)/使用Thread.currentThread().getName()获取主线程名字System.out.println(Thread.currentThread().getName()+:+i);12.2.3使用Callable和Future接口 上述代码先定义一
26、个Task类,该类实现Callable接口并重写call()方法,call()的返回值为整型,因此Callable接口中对应的泛型限制为Integer,即Callable。在main()方法中,先创建FutureTask类的对象task,该对象包装Task类;再创建Thread对象并启动线程;最后调用FutureTask对象task的get()方法获取子线程执行结束后的返回值。整个程序所实现的功能与前两种方式一样,只是增加了子线程返回值。程序运行结果如下:子线程:0 子线程:1 子线程:2 .子线程返回值:子线程返回值:1010 main:1100 main:1101 12.2.3使用Call
27、able和Future接口 从Java 8开始,可以直接使用Lambda表达式创建Callable对象,下述案例示例通过Lambda表达式创建Callable对象,代码如下所示。【代码12.5】LambdaCallableExample.javapackage com;import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;public class LambdaCallableExample public static void main(String args)/使用Lambda表达式创建Call
28、able对象/使用FutureTask类包装Callable对象FutureTask task=new FutureTask(Callable)()-(Callable)()-intint i i=0;=0;for(;for(;i i 10;10;i i+)+)System.out.printlnSystem.out.println(Thread.currentThreadThread.currentThread().().getNamegetName()+:+()+:+i i););/call()/call()方法可以有返回值方法可以有返回值return return i i;);12.2.
29、3使用Callable和Future接口/创建线程,使用FutureTask对象task作为Thread对象的target,/并调用start()方法启动线程Thread td=new Thread(task,子线程);td.start();/调用FutureTask对象task的get()方法获取子线程执行结束后的返回值try System.out.println(子线程返回值:+task.get();catch(Exception e)e.printStackTrace();/主线程任务for(int i=1100;i 1110;i+)/使用Thread.currentThread().g
30、etName()获取主线程名字System.out.println(Thread.currentThread().getName()+:+i);上述代码加粗部分就是Lambda表达式,可以直接使用Lambda表达式创建Callable对象,而无须先创建Callable实现类,但Lambda表达式必须在jdk1.8版本后才可以运行。在Java API中,定义的FutureTask类实际上直接实现RunnableFuture接口,而RunnableFuture接口继承Runnable和Future两个接口,因此FutureTask类即实现了Runnable接口,又实现了Future接口。12.2.
31、3第第3 3节节part线程的生命周期 线程具有生命周期,当线程被创建并启动后,不会立即进入执行状态,也不会一直处于执行状态。在线程的生命周期中,要经过5种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。线程状态之间的转换如图12-3所示。线程的生命周期本节介绍12.3.1新建和就绪状态 当程序使用new关键字创建一个线程之后,该线程就处于新建状态,此时与其他Java对象一样,仅由JVM为其分配内存并初始化。新建状态的线程没有表现出任何动态特征,程序也不会执行线程的执行体。当线程对象调用start()方法之后,线程就处于就绪状态
32、,相当于“等待执行”。此时,调度程序就可以把CPU分配给该线程,JVM会为线程创建方法调用栈和程序计数器。处于就绪状态的线程并没有开始运行,只是表示该线程准备就绪等待执行。注意只能对新建状态的线程调用start()方法,即new完一个线程后,只能调用一次start()方法,否则将引发IllegalThreadStateException异常。下述案例示例了新建线程重复调用start()方法引发异常,代码如下所示。新建和就绪状态新建和就绪状态【代码12.6】IllegalThreadExample.javapackage com;public class IllegalThreadExample
33、 public static void main(String args)/创建线程Thread t=new Thread(new Runnable()public void run()for(int i=0;i10;i+)System.out.print(i+););t.start();t.start();上述代码三次调用start()方法,多次启动线程,因此会引发IllegalThreadStateException异常。运行结果可能如下所示:Exception in thread main java.lang.IllegalThreadStateException 0 1 2 3 4 5
34、 6 7 8 9 at java.lang.Thread.start(Unknown Source)at com.IllegalThreadExample.main(IllegalThreadExample.java:12)12.3.112.3.2运行和阻塞状态 处于就绪状态的线程获得CPU后,开始执行run()方法的线程执行体,此时该线程处于运行状态。如果计算机的CPU是单核的,则在任何时刻只有一个线程处于运行状态。一个线程开始运行后,不可能一直处于运行状态。线程在运行过程中需要被中断,目的是使其他线程获得执行的机会,线程调度的细节取决于底层平台所采用的策略。目前UNIX系统采用的是时间片算
35、法策略,Windows系统采用的则是抢占式策略,另外一种小型设备(手机)则可能采用协作式调度策略。对于采用抢占式策略的系统而言,系统会给每个可执行的线程一个小时间段来处理任务;当该时间段用完后,系统就会剥夺该线程所占用的资源,让其他线程获得执行的机会。在选择下一个线程时,系统会考虑线程的优先级。运行和阻塞状态运行和阻塞状态当线程出现以下情况时,会进入阻塞状态:(1)调用sleep()方法,主动放弃所占用的处理器资源;(2)调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞;(3)线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有;(4)执行条件还未满足,调用wait()方法使线
36、程进入等待状态,等待其他线程的通知;(5)程序调用了线程的suspend()方法将该线程挂起,但该方法容易导致死锁,因此应该尽量避免使用。12.3.2运行和阻塞状态 正在执行的线程被阻塞之后,其他线程就可以获得执行的机会,被阻塞的线程会在合适的时机重新进入就绪状态,等待线程调度器再次调度。当线程出现如下几种情况时,线程可以解除阻塞进入就绪状态:(1)调用sleep()方法的线程经过了指定的时间;(2)线程调用的阻塞式IO方法以经返回;(3)线程成功地获得了同步监视器;(4)线程处于等待状态,其他线程调用notify()或notifyAll()方法发出了一个通知时,则线程回到就绪状态;(5)处于
37、挂起状态的线程被调用了resume()恢复方法。12.3.2运行和阻塞状态 在线程运行的过程中,可以通过sleep()方法使线程暂时停止运行,进入休眠状态。在使用sleep()方法时需要注意以下两点:(1)sleep()方法的参数是以毫秒为基本单位,例如sleep(2000)则休眠2秒钟;(2)sleep()方法声明了InterruptedException异常,因此调用sleep()方法时要么放在trycatch语句中捕获该异常并处理,要么在方法后使用throws显式声明抛出该异常。可以通过Thread类的isAlive()方法来判断线程是否处于运行状态。当线程处于就绪、运行和阻塞三种状态时
38、,isAlive()方法的返回值为true;当线程处于新建、死亡两种状态时,isAlive()方法的返回值为false。下述案例示例了线程的创建、运行和死亡三个状态,代码如下所示。12.3.2运行和阻塞状态【代码12.7】ThreadLifeExample.javapackage com;public class ThreadLifeExample extends Threadpublic void run()int sum=0;for(int i=0;i=100;i+)sum+=i;System.out.println(sum=+sum);public static void main(St
39、ring args)throws InterruptedException ThreadLifeExample tle=new ThreadLifeExample();System.out.println(新建状态isAlive():+tle.isAlive();tle.start();System.out.println(运行状态isAlive():+tle.isAlive();Thread.sleep(2000);System.out.println(线程结束isAlive():+tle.isAlive();12.3.2运行和阻塞状态 程序运行结果如下:新建状态isAlive():fals
40、e 运行状态isAlive():true sum=5050 线程结束isAlive():false 注意:线程调用wait()方法进入等待状态后,需其他线程调用notify()或notifyAll()方法发出通知才能进入就绪状态。使用suspend()和resume()方法可以挂起和唤醒线程,但这两个方法可能会导致不安全因素。如果对某个线程调用interrupt()方法发出中断请求,则该线程会根据线程状态抛出InterruptedException异常,对异常进行处理时可以再次调度该线程。12.3.2死亡状态线程结束后就处于死亡状态,结束线程有以下三种方式:(1)线程执行完成run()或cal
41、l()方法,线程正常结束;(2)线程抛出一个未捕获的Exception或Error;(3)调用stop()方法直接停止线程,该方法容易导致死锁,通常不推荐使用。死亡状态12.3.2死亡状态 主线程结束时,其他子线程不受任何影响,并不会随主线程的结束而结束。一旦子线程启动起来,子线程就拥有和主线程相同的地位,子线程不会受主线程的影响。为了测试某个线程是否死亡,可以通过线程对象的isAlive()方法来获得线程状态,当方法返回值为false时,线程处于死亡或新建状态。不要试图对一个已经死亡的线程调用start()方法使其重新启动,线程死亡就是死亡,该线程不可再次作为线程执行。Thread类中的jo
42、in()方法可以让一个线程等待另一个线程完成后,继续执行原线程中的任务。当在某个程序执行流中调用其他线程的join()方法时,当前线程将被阻塞,直到另一个线程执行完为止。join()方法通常由使用线程的程序调用,当其他线程都执行结束后,再调用主线程进一步操作。下述案例示例了join()方法的使用,代码如下所示。12.3.2死亡状态【代码12.8】JoinExample.javapackage com;class JoinThread extends Threadpublic JoinThread()super();public JoinThread(String str)super(str);
43、public void run()for(int i=0;i10;i+)System.out.println(this.getName()+:+i);12.3.2死亡状态public class JoinExample public static void main(String args)/创建子线程JoinThread t1=new JoinThread(被Join的子线程);/启动子线程t1.start();/等待子线程执行完毕try t1.join();catch(InterruptedException e)/TODO Auto-generated catch blocke.prin
44、tStackTrace();/输出主线程名System.out.println(主线程名为:+Thread.currentThread().getName();/子线程已经处于死亡状态,其isAlive()方法返回值为falseSystem.out.println(子线程死亡状态isAlive():+t1.isAlive();/再次启动子线程,抛出异常 t1.start();12.3.2死亡状态 上述代码中开始调用了线程的join()方法,最后又对死亡状态的线程再次调用start()方法,运行结果如下所示:.被Join的子线程:8 被Join的子线程:9 主线程名为:main 子线程死亡状态i
45、sAlive():false Exception in thread main java.lang.IllegalThreadStateExceptionat java.lang.Thread.start(Unknown Source)at com.JoinExample.main(JoinExample.java:33)在上述代码中,注销掉join()方法的调用和对死亡状态线程的start()方法的再次调用,运行结果可能如下:主线程名为:main 被Join的子线程:0 被Join的子线程:1 子线程死亡状态isAlive():true 被Join的子线程:2 被Join的子线程:3 12.
46、3.2第第4 4节节part线程的优先级 每个线程执行时都具有一定的优先级,线程的优先级代表该线程的重要程度。当有多个线程同时处于可执行状态并等待获得CPU处理器时,系统将根据各个线程的优先级来调度各线程,优先级越高的线程获得CPU时间的机会越多,而优先级低的线程则获得较少的执行机会。每个线程都有默认的优先级,其优先级都与创建该线程的父线程的优先级相同。在默认情况下,主线程具有普通优先级,由主线程创建的子线程也具有普通优先级。Thread类提供三个静态常量来标识线程的优先级:(1)MAX_PRIORITY:最高优先级,其值为10;(2)NORM_PRIORITY:普通优先级,其值为5;(3)M
47、IN_PRIORITY:最低优先级,其值为1。线程的优先级线程的优先级 Thread类提供了setPriority()方法来对线程的优先级进行设置,而getPriority()方法来获取线程的优先级。setPriority()方法的参数是一个整数(110),也可以使用Thread类提供的三个优先级静态常量。线程的优先级高度依赖于操作系统,并不是所有的操作系统都支持Java的10个优先级,例如Windows 2000仅提供7个优先级。因此,尽量避免直接使用整数给线程指定优先级,提倡使用MAX_PRIORITY、NORM_PRIORITY和MIN_PRIORITY三个优先级静态常量。另外,优先级并
48、不能保证线程的执行次序,因此应避免使用线程优先级作为构建任务执行顺序的标准。下述案例示例了线程优先级的设置及使用,代码如下所示。线程的优先级【代码12.9】PriorityExample.javapackage com;class MyPriorityThread extends Thread public MyPriorityThread()super();public MyPriorityThread(String name)super(name);public void run()for(int i=0;i 10;i+)System.out.println(this.getName()+
49、,其优先级是:+this.getPriority()+,循环变量的值为:+i);线程的优先级public class PriorityExample public static void main(String args)/输出主线程的优先级System.out.println(主线程的优先级:+Thread.currentThread().getPriority();/创建子线程,并设置不同优先级MyPriorityThread t1=new MyPriorityThread(高级);t1.setPriority(Thread.MAX_PRIORITY);MyPriorityThread t
50、2=new MyPriorityThread(普通);t2.setPriority(Thread.NORM_PRIORITY);MyPriorityThread t3=new MyPriorityThread(低级);t3.setPriority(Thread.MIN_PRIORITY);MyPriorityThread t4=new MyPriorityThread(指定值级);t4.setPriority(4);/启动所有子线程t1.start();t2.start();t3.start();t4.start();线程的优先级程序运行执行结果可能如下:主线程的优先级:5普通,其优先级是:5