JAVA多线程详解文档格式.docx
- 文档编号:19133004
- 上传时间:2023-01-04
- 格式:DOCX
- 页数:20
- 大小:123.93KB
JAVA多线程详解文档格式.docx
《JAVA多线程详解文档格式.docx》由会员分享,可在线阅读,更多相关《JAVA多线程详解文档格式.docx(20页珍藏版)》请在冰豆网上搜索。
一个线程“创建→工作→死亡”的过程称为线程的生命周期。
线程生命周期共有五个状态:
新建状态、就绪状态、运行状态、阻塞状态和死亡状态。
1.新建状态
新建状态是指创建了一个线程,但它们还没有启动。
处于新建状态的线程对象,只能够被启动或者终止。
例如,以下代码使线程myThread处于新建状态:
ThreadmyThread=newThread();
2.就绪状态
就绪状态是当线程处于新建状态后,调用了start()方法,线程就处于就绪状态。
就绪状态线程具备了运行条件,但尚未进入运行状态。
处于就绪状态的线程可以多个,这些就绪状态的线程将在就绪队列中排队,等待CPU资源。
就绪状态的线程通过线程调度获得CPU资源变成运行状态。
例如,以下代码使myThread处于就绪状态。
myThread.start();
3.运行状态
运行状态是某个就绪状态的线程获得CPU资源,正在运行。
如果有更高优先级的线程进入就绪状态,则该线程将被迫放弃对CPU的控制器,加入就绪状态。
使用yield()方法可以使线程主动放弃CPU。
线程也可能由于结束或执行stop()方法进入死亡状态。
每个线程对象都有一个run()方法,当线程对象开始执行时,系统就调用对象的run()方法。
4.阻塞状态
柱塞状态是正在运行的线程遇到某个特殊情况。
例如,延迟、挂起、等待I/O操作完成等,进入柱塞状态的线程让出CPU,并暂时停止自己的执行。
线程进入柱塞状态后,就一直等待,直到引起柱塞的原因被消失,线程有转入就是状态,重新进入就绪列队排列。
当线程再次变成运行状态时,将从暂停处开始继续运行。
线程从柱塞状态恢复到就绪状态,有三种途径:
(1)自动恢复:
(2)用resume()方法恢复:
(3)用notif()或nitifyAll()方法通知恢复。
也可能因为别的线程强制某个处于阻塞状态的线程终止,该线程就从阻塞状态进入死亡状态。
5.死亡状态
死亡状态是指线程不再具有继续运行的能力,也不能再转到其他状态。
一般有两种情况使一个线程终止,进入死亡状态。
(1)线程完成了全部工作,即执行完run()方法的最后一条语句。
(2)线程被提前强制性终止。
图8.1是线程的生命周期图,图中给出从一种状态转变成另一种状态的各种可能的原因。
8.1.2线程调度与优先级
Java提供一个线程调度器来监视和控制就绪状态的线程。
线程的调度策略采用抢占式,优先级高的线程比优先级低的线程优先执行。
在优先级相同的情况下,就按“先到先服务”的原则。
线程的优先级用数值表示,数值越大优先级越高(范围1~10)。
每个线程根据继承特性自动从父进程获得一个线程的优先级,也可在程序中重新设置。
对于任务较紧急的重要线程,可安排较高的优先级。
相反,则给一个较低的优先级。
每个Java程序都有一个默认的主线程,就是通过JVM(JavaVirtualMachine,Java虚拟机)启动的第一个线程。
对于应用程序,主线程执行的是main()方法。
对于Applet,主线程是指浏览器加载并执行小应用程序的哪一个线程。
子程序是由应用程序创建的线程。
另有一种线程称为守护线程(Daemon),这是一种用于监视其他线程工作的服务线程,它的优先级最低。
8.2Thread类和Runnable接口
Java程序实现多线程应用有两种途径:
●一是继承Thread类声明Thread子类,用Thread子类创建线程对象。
●二是在类中实现Runnable接口,在类中提供Runnable接口的run()方法。
无论用哪种方法,都需要Java基础类库中的Thread类及其方法的支持。
程序员能控制的关键性工作有两个方面:
●一是编写线程的run()方法;
●二是建立线程实例。
8.2.1Thread类
Thread类是用来创建线程和提供线程操作的类。
1.Thread类提供的方法
Thread类为创建线程和线程控制提供以下常用的方法:
(1)Thread(),创建一个线程。
线程名按创建顺序为Thread_1、Thread_2·
·
等。
(2)Thread(Stringm),创建一个以m命名的线程。
(3)Thread(Runnabletarget),创建线程,参数target是创建线程的目标。
目标是一个对象,对象的类要实现Runnable接口,类中给出接口的run()方法。
(4)publicThread(Runnabletarget,Stringm),创建线程,参数target是创建线程的目标。
m是线程名。
(5)Thread(ThreadGroupg,Stringm),创建一个以m命名的线程,该线程属于指定的线程组g。
(6)Thread(ThreadGroupg,Runnabletarget),创建一个属于指定线程组g的线程,target是线程的目标。
(7)Thread(ThreadGroupg,Runnabletarget,Stringm),创建一个线程,名为m,属于指定线程组g,target是线程的目标。
(8)getPriority(),获得线程的优先级。
(9)setPriority(intp),设定线程的优先级为p。
线程创建时,子线程继承父线程的优先级。
优先级的数值越大优先级越高(缺省是5)。
常用以下3个优先级:
●Thread.MIN_PRIORITY(最低)
●Thread.MAX_PRIORITY(最高)
●Thread.NORM_PRIORITY(标准)
(A)start(),启动线程,让线程从新建状态到就绪状态。
(B)run(),实现线程行为(操作)的方法。
(C)sleep(intdt)让线程休眠dt时间,单位是毫秒。
例如代码sleep(100);
让线程休眠100毫秒时间段。
在以后的这个时间段中,其他线程就会有机会被执行。
当休眠时间一到,将重新到就绪队列排序。
由于sleep()方法可能会产生InterruptExpiration异常,应将sleep方法写在try块中,并用catch块处理异常。
sleep()方法是static方法,不可重载。
(D)currentThread(),获得当前正在占有CPU的能个线程。
(E)getName(),获取线程的名字。
(F)setName(),设置线程的名字。
(G)isAlive(),返回boolean类型值,查询线程是否还活跃。
(H)destroy(),强制线程生命周期结束。
(I)stop(),强制线程生命周期结束,并完成一些清理工作,及抛出异常。
有时,小应用程序的界面已从屏幕消失,但并没有停止线程的执行,会一直到浏览器关闭才结束。
因此要在小应用程序的stop()方法中终止线程的执行。
(J)suspend(),挂起线程,处于不可运行的阻塞状态。
(K)resume(),恢复挂起的线程,重新进入就绪队列排队。
(L)yield(),暂停当前正在执行的线程,若就绪队列中有与当前线程同优先级的线程,则当前线程让出CPU控制权,移到就绪队列的队尾。
若队列中没有同优先级或更高优先级的线程,则当前线程继续执行。
2.用Thread子类实现多线程
用Thread子类实现多线程,得先声明一个Thread类的子类,并在子类中重新定义run()方法。
当程序需要建立线程时,就可以创建Thread子类的实例,并让创建的线程调用start()方法,这时,run()方法将自动执行。
【例8.1】应用程序用Thread子类实现多线程。
在main()方法中创建了两个线程threadA和threadB,它们分别是Thread子类threadA和threadB的实例,并分别调用它们的start()方法启动它们。
threadA先调用start()方法,类threadA中的run()方法将自动执行。
由于threadA线程先执行,它的run()方法输出“我是threadA”和当时的时间后,threadA线程主动休息2000毫秒,让出CPU。
这时正在等待CUP的threadB线程获得CPU资源,执行它的run()方法,输出“我是threadB”和当时的时间,threadB线程主动让出CPU,1000毫秒后又来排队等待CPU资源。
过了1000毫秒后,这时threadA线程还没有“醒了”,因此有轮到threadB执行。
再次输出“我是threadB”·
importjava.util.Date;
publicclassExample8_1{
staticAthreadthreadA;
staticBthreadthreadB;
publicstaticvoidmain(Stringargs[]){
threadA=newAthread();
threadB=newBthread();
threadA.start();
threadB.start();
}
}
classAthreadextendsThread{
publicvoidrun(){
DatetimeNew;
//为了能输出当时的时间
for(inti=0;
i<
=5;
i++){
timeNew=newDate();
System.out.println("
我是threadA:
"
+timeNew.toString());
try{
sleep(2000);
}catch(InterruptedExceptione){}
}
classBthreadextendsThread{
我是threadB:
sleep(1000);
以下是例8.1程序执行的输出结果,从输出清单中可发现两线程的输出频度的差异。
ThuFeb1617:
26:
06CST2012
07CST2012
08CST2012
09CST2012
10CST2012
11CST2012
12CST2012
14CST2012
16CST2012
8.2.2Runnable接口
Java.lang.Runnable接口,只有run()方法需要实现。
一个实现Runnable接口的类数据上定义了一个在主线程之外的新线程的操作。
用Runnable接口实现多线程的主要工作是:
声明实现Runnable接口的类,在类内实现run()方法;
并在类内声明线程对象,在init()方法或start()方法中创建新线程,并在start()方法中启动新线程。
【例8.2】小应用程序通过Runnable接口创建线程。
在类的start()方法中用构造方法Thread(this)创建了一个新的线程。
this代表着该类的对象作为新线程的目标,因此类必须为这个新创建的线程实现Runnable接口,即提供run()方法。
在start()方法中构造了一个名为myThread的线程,并调用Thread类的start()方法来启动这个程序。
这样,run()方法被执行,除小应用程序的主线程外,又开始了一个新线程myThread。
在例子中,run()方法在睡眠1秒后,调用repaint()方法重绘Applet窗口。
在paint()方法中,在文本区输出线程睡眠的次数,并用随机产生的颜色和半径涂一个圆块。
importjava.applet.*;
importjava.awt.*;
importjavax.swing.*;
publicclassExample8_2extendsAppletimplementsRunnable{
ThreadmyThread=null;
//声明一个线程对象
JTextAreat;
intk;
publicvoidstart(){
t=newJTextArea(20,20);
add(t);
k=0;
setSize(500,400);
if(myThread==null){//重新进入小程序时,再次创建线程myThread
myThread=newThread(this);
//创建新线程
myThread.start();
//启动新线程
while(myThread!
=null){
myThread.sleep(1000);
k++;
repaint();
publicvoidpaint(Graphicsg){
doublei=Math.random();
if(i<
0.5)
g.setColor(Color.yellow);
else
g.setColor(Color.blue);
g.fillOval(10,10,(int)(100*i),(int)(100*i));
t.append("
我在工作,已休息了"
+k+"
次\n"
);
publicvoidstop(){
if(myThread!
myThread.stop();
myThread=null;
【例8.3】小应用程序创建两个线程,一个顺序针画圆,另一个逆时针画圆。
例子使用容器类方法getGraphics()获得Graphics对象,给线程作为画笔使用。
由于两个线程睡眠时间不同,画图的速度不一致。
程序为了解决图形移动显示时的闪烁问题,采用底色重画原图,将原图擦去的办法。
publicclassExample8_3extendsAppletimplementsRunnable{
ThreadredBall,blueBall;
GraphicsredPen,bluePen;
intblueSeta=0,redSeta=0;
publicvoidinit(){
setSize(250,200);
redBall=newThread(this);
blueBall=newThread(this);
redPen=getGraphics();
bluePen=getGraphics();
redPen.setColor(Color.red);
bluePen.setColor(Color.blue);
setBackground(Color.gray);
redBall.start();
blueBall.start();
intx,y;
while(true){
if(Thread.currentThread()==redBall){
x=(int)(80.0*Math.cos(3.1415926/180.0*redSeta));
y=(int)(80.0*Math.sin(3.1415926/180.0*redSeta));
redPen.setColor(Color.gray);
//用底色画图,擦除原先所画原点
redPen.fillOval(100+x,100+y,10,10);
redSeta+=3;
if(redSeta>
=360)redSeta=0;
redPen.setColor(Color.red);
try{redBall.sleep(20);
catch(InterruptedExceptione){}
}elseif(Thread.currentThread()==blueBall){
x=(int)(80.0*Math.cos(3.1415926/180.0*blueSeta));
y=(int)(80.0*Math.sin(3.1415926/180.0*blueSeta));
bluePen.setColor(Color.gray);
bluePen.fillOval(150+x,100+y,10,10);
blueSeta-=3;
if(blueSeta>
=360)blueSeta=0;
bluePen.setColor(Color.blue);
}
8.3线程互斥和同步
通常情况下,程序中的多个线程是相互协调和相互联系的,多线程之间有互斥和同步。
8.3.1线程互斥
先看线程之间需要互斥的情况。
设有若干线程共享某个变量,且都对变量有修改。
如果它们之间不考虑相互协调工作,就会产生混乱。
比如,线程A和B共有变量x,都对x执行增1操作。
由于A和B没有协调,两线程对x的读取、修改和写入操作会相互交叉,可能两个线程读取同样的x值,一个线程将修改后的x新值写入到x后,另一个线程也把自己对x的修改后的新值写入到x。
这样,x只记录一个线程的修改作用。
【例8.4】应用程序说明多线程共享变量,因设有互相协调生产不正确结果。
主线程创建了20个线程,它们都取变量的值,经累加后,将新值存回变量。
待这些线程执行结束,产生不正确的结果。
publicclassExample8_4{
MyResourceClassmrc=newMyResourceClass();
Thread[]aThreadArray=newThread[20];
System.out.println("
\t刚开始的值是:
+mrc.getInfo());
\t预期的正确结果是:
+20*1000*50);
\t多个线程正在工作,请稍等!
20;
aThreadArray[i]=newThread(newMyMultiThreadClass(mrc));
aThreadArray[i].start();
WhileLoop:
while(true){
for(inti=0;
i++)
if(aThreadArray[i].isAlive())continueWhileLoop;
break;
\t最后的结果是:
classMyMultiThreadClassimplementsRunnable{
MyResourceClassUseInteger;
MyMultiThreadClass(MyResourceClassmrc){
UseInteger=mrc;
publicvoidr
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- JAVA 多线程 详解