JAVA多线程详解文档格式.docx

上传人:b****5 文档编号:19133004 上传时间:2023-01-04 格式:DOCX 页数:20 大小:123.93KB
下载 相关 举报
JAVA多线程详解文档格式.docx_第1页
第1页 / 共20页
JAVA多线程详解文档格式.docx_第2页
第2页 / 共20页
JAVA多线程详解文档格式.docx_第3页
第3页 / 共20页
JAVA多线程详解文档格式.docx_第4页
第4页 / 共20页
JAVA多线程详解文档格式.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

JAVA多线程详解文档格式.docx

《JAVA多线程详解文档格式.docx》由会员分享,可在线阅读,更多相关《JAVA多线程详解文档格式.docx(20页珍藏版)》请在冰豆网上搜索。

JAVA多线程详解文档格式.docx

一个线程“创建→工作→死亡”的过程称为线程的生命周期。

线程生命周期共有五个状态:

新建状态、就绪状态、运行状态、阻塞状态和死亡状态。

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 文化宗教

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1