高清版21天学通JAVA第十三天.docx

上传人:b****7 文档编号:11215625 上传时间:2023-02-25 格式:DOCX 页数:45 大小:83.83KB
下载 相关 举报
高清版21天学通JAVA第十三天.docx_第1页
第1页 / 共45页
高清版21天学通JAVA第十三天.docx_第2页
第2页 / 共45页
高清版21天学通JAVA第十三天.docx_第3页
第3页 / 共45页
高清版21天学通JAVA第十三天.docx_第4页
第4页 / 共45页
高清版21天学通JAVA第十三天.docx_第5页
第5页 / 共45页
点击查看更多>>
下载资源
资源描述

高清版21天学通JAVA第十三天.docx

《高清版21天学通JAVA第十三天.docx》由会员分享,可在线阅读,更多相关《高清版21天学通JAVA第十三天.docx(45页珍藏版)》请在冰豆网上搜索。

高清版21天学通JAVA第十三天.docx

高清版21天学通JAVA第十三天

 

第13章

 

多线程

 

·多线程是Java中的并发机制,表示能够在同一时间内同时执行多

个操作。

在日常生活中,边上网边听歌就是一个多线程。

随着CPU

进入双核,甚至多核时代,多线程的优势越来越明显。

Java本身

就是一门支持多线程的语言,在Java中使用多线程是很方便的,

同样也是很高效的。

通过本章的学习,读者应该能够完成如下几

个目标。

·了解什么是多线程。

·熟练掌握如何定义和使用多线程。

·了解多线程的生命周期。

·掌握多线程的调用的几个情况。

·了解多线程的同步问题。

 

1-1

 

13.1多线程简介

 

·多线程就好像日常生活中同时做几件事一样,例如早上起床,要

烧水洗脸,在烧水时就可以刷牙,还可以边刷牙边看早间新闻,

这样就同时做着烧水、刷牙、看电视三件事。

多线程也是一样的

,在同一时刻有可能在执行多个线程,这样能够更好地提高办事

效率。

·在实际开发中也是在很多地方使用多线程的,例如在很多网站中

,当用户注册后,系统一方面会通知用户已经注册成功,一方面

向用户在注册时填写的Email中发送邮件。

这里就需要使用多线程

,如果使用的是单线程,系统就会向用户注册的Email中发送邮件

后才显示用户注册成功,由于发送邮件可能需要很长的时间,从

而影响整个注册进度。

·在前面的学习中,虽然没有使用多线程,但是同样使用到了线程

的知识。

在每一个程序中的main方法就是一个线程,它一般被称

为主线程。

在主线程中可以启动多个子线程来执行。

 

1-2

 

13.2定义线程和创建线程对象

 

·在上一小节中讲解了什么是多线程,在本节中就来讲解怎样来定

义线程和如何创建线程对象。

定义线程有两种方法,一种是继承

Thread类,一种是实现Runnable接口,这两种方法是存在各自优

缺点的。

和定义线程对应的就是创建线程对象,也是有两种方法

在本节中就来学习使用这两种方法来定义线程,以及相对应的

创建线程对象。

 

1-3

 

13.2.1继承Thread类定义线程

 

·定义一个线程可以通过继承Thread类来实现,这是一种相对简单

的定义线程的方法。

在Thread类中具有一个run方法,在定义的线

程中需要重写这个方法。

在重写的run方法中,可以定义该线程所

要执行的语句。

当线程启动时,run方法中的程序就成为一条独立

的执行线程。

·【范例】示例代码是一个通过继承Thread类定义线程的程序。

·示例代码

·01publicclassXianCheng1extendsThread

·02{

·03publicvoidrun()

·04{

·05System.out.println("通过继承Thread定义线程");

·06}

·07}

 

1-4

 

1-5

 

·

 

·

 

·

 

·

·

 

该程序是无法运行的,因为没有main方法,也就是没有启动线程的方法

在该程序中创建了一个线程类继承于Thread类,并且在该类中重写了

run方法,在其中定义了该线程的功能是显示一条语句。

注意:

重写的run方法也是可以作为一般的方法来调用的,但是这种调用

并不是作为一个线程出现的,它只是主线程中的一部分。

同样,run方法

也是可以被重载的,但是重载后的run方法不作为一个线程,也是主线程

的一部分。

讲解完定义线程后,就可以来学习如何创建线程对象。

通过继承Thread

类创建线程,是很容易创建线程对象的。

在这种定义线程的方法中,创

建线程对象和创建普通对象是一样的。

下面是创建示例代码13-1中线程

对象的代码。

XianCheng1xc=newXianCheng1();

从创建线程对象的程序可以看出,创建线程对象的方法和创建普通对象

的方法是一样的。

但是这只是对于使用继承Thread类创建线程的方法来

说的。

 

 

13.2.2实现Runnable接口定义线程

 

1-6

 

·

 

·

·

·

·

·

·

·

·

 

定义线程除了通过继承Thread类来实现,还可以通过实现Runnable接口

来实现。

在Runnable接口中具有一个抽象的run方法,在实现Runnable接

口时,需要实现该run方法。

该run方法就会作为一个执行线程的方法。

【范例】示例代码是一个通过实现Runnable接口定义线程的程序。

·示例代码

01publicclassXianCheng2implementsRunnable

02{

03publicvoidrun()

04{

05System.out.println("通过实现Runnable接口定义线程");

06}

07}

 

 

1-7

 

·

 

·

 

·

 

·

·

·

 

1是通过继承Thread类定义线程,2是通过实现Runnable接口来定义线程

这两种方法中都需要定义一个run方法,不管该方法是通过重写父类方

法,还是实现接口方法。

run方法是一个线程的入口,是线程必须具有的

在使用通过实现Runnable接口定义的线程中,要想创建线程对象就不是

很容易做到的。

因为直接创建类对象,创建的并不是一个线程对象。

想创建线程对象,必须要借助Thread类。

Thread类具有4个构造器,最常用的就是具有一个参数,该参数是实现

Runnable接口类对象的构造器。

创建线程对象的程序如下所示。

XianCheng2xc=newXianCheng2();

Threadt=newThread(xc);

在该程序中,首先创建了一个实现Runnable接口的类对象,然后将该对

象作为Thread类的参数,从而创建了一个线程对象。

创建的类对象是可

以作为多个Thread类构造器参数的,这样就创建了多个线程。

这一点将

在以后的学习中多次使用。

 

 

13.3运行线程

 

·在上一节中学习了如何定义线程,并且知道了如何创建线程对象

对这些都了解后就需要来学习如何运行线程。

在本节中分为两

个小节来讲解,先来学习如何启动线程,然后讲解如何运行多个

线程。

 

1-8

 

13.3.1启动线程

 

1-9

 

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

 

有些读者会认为启动线程就是调用线程类中的run方法。

例如示例代码13-3中所演

示的。

【范例】示例代码是一个错误的启动线程的程序。

·示例代码

01classMyRunnableimplementsRunnable

02{

03//定义一个run线程方法

04publicvoidrun()

05{

06System.out.println("这是一个错误的启动线程的程序");

07}

08}

09publicclassXianCheng3

10{

11publicstaticvoidmain(Stringargs[])

12{

13MyRunnablemr=newMyRunnable();

14mr.run();//调用run方法

15}

16}

 

 

·从该程序可以看出,run方法是可以通过方法

调用来执行的,但是这并不代表创建了一个新

线程。

这是一个错误的启动线程的方法。

·如果想正确地启动一个线程,需要调用线程对象的start方法,下

面通过程序来演示如何正确的启动一个线程。

 

1-10

 

一个正确的启动线程的程序

 

·

·

·

·

·

·

·

·

·

·

·

·

·

·

 

01

02

03

04

05

06

07

08

09

10

11

12

13

14

 

classMyRunnableimplementsRunnable

{

//定义一个run线程方法

publicvoidrun()

{

System.out.println("这是一个正确的启动线程的程序");

}

}

publicclassXianCheng4

{

publicstaticvoidmain(Stringargs[])

{

MyRunnablemr=newMyRunnable();

Threadt=newThread(mr);

·

·

·

15

16

17

 

}

}

t.start();

//启动线程

 

1-11

 

·【代码解析】第一次看到该程序时,读者可能会感到有些奇怪,

为什么调用的是start方法,而执行的是run方法,这就是Java对多

线程的设计。

在调用start方法后,就启动了线程,该线程是和

main方法并列执行的线程。

这样该程序就变为一个多线程程序。

·注意:

线程只能被启动一次,也就是只能调用一次start方法。

多次启动线程,也就是多次调用start方法时,就会发生异常。

 

1-12

 

13.3.2同时运行多个线程

 

·学习了如何启动线程,接下来就来学习如何同时运行多个线程。

首先通过示例代码来看一下如何同时运行多个线程。

·27

·28

·29

·30

·31

·32

publicstaticvoidmain(Stringargs[])

{

MyRunnable1mr1=newMyRunnable1();

MyRunnable2mr2=newMyRunnable2();

Threadt1=newThread(mr1);

Threadt2=newThread(mr2);

·33

·34

·35

 

}

t1.start();

t2.start();

//启动第一个线程

//启动第二个线程

·36}

1-13

 

·【代码解析】在示例代码13-6中首先定义了两个实现Runnable接

口的类,在两个类中都定义了run方法,显示多个不同的符号。

运行结果中可以看出,不同的符号是交替显示的。

·在同时运行多个线程时,运行结果不是唯一的,因为有很多不确

定的因素。

首先先执行哪一个线程就是不确定的,线程间交替也

是不确定的。

但是确定的是每一个线程都将启动,每一个线程都

执行结束。

 

1-14

 

13.4线程生命周期

 

·线程是存在生命周期的。

线程的生命周期分为五种不同的状态,

分别是新建状态、准备状态、运行状态、等待/阻塞状态和死亡状

态。

在本节中就来对每一个状态进行讲解。

 

1-15

 

13.4.1新建状态

 

·当一个线程对象被创建后,线程就处于新建状态。

在新建状态中

的线程对象从严格意义上看还只是一个普通的对象,它还不是一

个独立的线程。

处于新建状态中的线程被调用start方法后就会进

入准备状态。

从新建状态中只能进入准备状态,并且不能从其他

状态进行新建状态。

新建状态是线程生命周期的第一个状态。

 

1-16

 

13.4.2准备状态

 

·处于新建状态中的线程被调用start方法就会进入准备状态。

处于

准备状态下的线程随时都可能被系统选择进入运行状态,从而执

行线程。

可能同时有多个线程处于准备状态,对于哪一个线程将

进入运行状态是不确定的。

线程从新建状态进入到准备状态后是

不可能再进入新建状态的。

在等待/阻塞状态中的线程被解除等待

和阻塞后将不直接进入运行状态,而是首先进入准备状态,让系

统来选择哪一个线程进入运行状态。

 

1-17

 

13.4.3运行状态

 

·处于准备状态中的线程一旦被系统选中,使线程获取了CPU时间,

就会进入运行状态。

在运行状态中将执行线程类run方法中的程序

语句。

线程进入运行状态后也不是一下执行结束的,线程在运行

状态下随时都可能被调度程序调度回准备状态。

在运行状态下还

可以让线程进入到等待/阻塞状态。

在通常的单核CPU中,在同一

时刻只有一个线程处于运行状态的。

在多核的CPU中,就可能两个

线程或者更多的线程同时处于运行状态,这也是多核CPU运行速度

快的原因。

 

1-18

 

13.4.4等待/阻塞状态

 

·在Java中定义了许多线程调度的方法,包括睡眠、阻塞、挂起和

等待,这些方法将在后面的调度章节中讲解。

使用这些方法都会

将处于运行状态的线程调度到等待/阻塞状态。

处于等待/阻塞状

态的线程被解除后,不会立即回到运行状态,而是首先进入准备

状态,等待系统的调度。

 

1-19

 

13.4.5死亡状态

 

·当线程中的run方法执行结束后,或者程序发生异常终止运行后,

线程会进入死亡状态。

处于死亡状态的线程不能再使用start方法

启动线程,这在前面的学习中已经学到了这一点。

但是这不代表

处于死亡状态的线程不能再被使用,它也是可以再被使用的,只

是将被作为普通的类来使用。

·注意:

线程生命周期的问题,有些读者会觉得很容易的。

线程生

命周期的问题在后面的学习中会经常使用到,只有能充分了解线

程的生命周期,才能更好地理解后面的内容。

 

1-20

 

13.5线程的调度

 

·通过系统自动调度,线程的执行顺序是没有保障的。

在Java中定

义了一些线程调度的方法,使用这些方法在一定程序上对线程进

行调度,使用这些方法只是给线程一个建议,具体是否能够成功

,也是没有保障的。

线程调度的方法有几个,包括睡眠方法、设

置优先级、让步方法等,在本节中就来学习这些方法的使用。

 

1-21

 

13.5.1睡眠方法

 

·当线程处于运行状态时,调用sleep睡眠方法将使线程从运行状态

进入等待/阻塞状态,从而使程序停止运行。

sleep睡眠方法是具

有一个时间参数的,当经过这么长时间后,线程将进入准备状态

,等待系统的调度。

从而可以看出,当线程调用睡眠方法后,要

想回到运行状态,需要的时间要比指定的睡眠时间长。

·sleep方法被重载,存在两种形式,sleep方法的基本语法格式如

下所示。

·publicstaticvoidsleep(longmillis)throws

InterruptedException;

·publicstaticvoidsleep(longmillis,intnanos)throws

InterruptedException;

 

1-22

 

·使用这两个sleep方法都能使线程进入睡眠状态,mills参数表示线

程睡眠的毫秒数,nanos参数表示线程睡眠的纳秒数。

sleep方法

是一个静态的方法,所以sleep方法不是依赖于某一个对象的,它

的位置是比较随意的。

当在线程中执行sleep方法,则该线程就进

入睡眠状态。

要想让某一个线程进入睡眠状态,并不是让该线程

调用sleep方法,而只是让该线程执行sleep方法。

sleep方法是可

能发生捕获异常的,所以在使用sleep方法时必须进行异常处理。

·注意:

sleep方法只是给线程一个调度的建议,是否调度成功是不

能确定的。

在该程序中只有两个线程,所以运行结果出现交替显

示的结果。

当程序中存在多个线程时,运行结果就可以发生变化

,甚至出现意外的结果。

 

1-23

 

13.5.2线程优先级

 

·在大部分的系统中,对进程的调度都是采用优先级的方式来进行

的。

在Java中对线程进行调度时,也是可以采用优先级来调度的

不同的线程可以具有不同的优先级,优先级高的线程就会占用

更多的CPU资源和被执行概率。

·Java中的优先级是采用从1到10来表示的,数字越大表示优先级越

高。

如果没有为线程设置优先级,则线程的优先级为5,这也是线

程的默认值。

但是对于子线程来说,它的优先级是和其父线程优

先级相同的。

·当需要对线程的优先级进行设置时,可以通过调用setPriority方法

来设置。

setPriority方法的语法格式如下所示。

·publicfinalvoidsetPriority(inti);

·其中参数i表示的就是优先级的等级数,它可以从1到10。

除了可

以使用数字来表示优先级,Java还在Thread类中定义了三个表示

优先级的常量。

MAX_PRIORITY表示线程的最高优先级,

NORM_PRIORITY表示线程的默认优先级,MIN_PRIORITY表示

线程的最低优先级。

1-24

 

13.5.3

 

yield让步方法

 

·在Java中具有两种线程让步方法,先来介绍第一种yield让步方法

yield让步方法是让线程让出当前CPU,而将CPU让给哪一个线程

是不确定的,由系统来进行选择。

使用yield让步方法的线程将从

运行状态进入到准备状态。

·注意:

yield让步操作是可能不成功的。

因为在线程中使用yield

方法,使该线程进入准备状态。

但是系统是有可能再次选择该线

程,使该线程进入运行状态的。

·yield让步方法的基本语法格式如下所示。

·publicstaticvoidyield();

·可以看出yield让步方法是一个静态方法,所以该方法也是和对象

无关的。

当在正在运行的线程中运行该方法时,该线程将回到准

备状态。

 

1-25

 

13.5.4

 

join让步方法

 

·

 

·

·

·

 

·

 

1-26

 

使用join让步方法,可以将当前线程的CPU资源让步给指定的线程。

join

让步方法的语法格式如下所示。

publicfinalvoidjoin()throwsInterruptedException;

publicfinalvoidjoin(longmills)throwsInterruptedException;

publicfinalvoidjoin(longmillis,intnanos)throws

InterruptedException;

join让步方法是具有三种形式的,没有参数表示指定的线程执行完成后

再执行其他线程,参数表示在参数的时间内执行让步给的执行线程。

join让步方法也是可能发生捕获异常的,所以在使用时要进行异常处理

 

 

13.6综合练习

 

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

 

1-27

 

1.判断下面程序是否能够正常运行。

01publicclassLianXi1extendsThread

02{

03publicvoidrun()

04{

05for(inti=0;i<5;i++)

06{

07System.out.println(i);

08}

09}

10publicstaticvoidmain(Stringargs[])

11{

12LianXi1lx=newLianXi1();

13lx.run();

14}

15}

 

 

13.6综合练习

 

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

 

1-28

 

2.判断下面的程序是否能够正常运行。

01publicclassLianXi2extendsThread

02{

03publicvoidrun()

04{

05for(inti=0;i<5;i++)

06{

07Thread.sleep(100);

08}

09}

10publicstaticvoidmain(Stringargs[])

11{

12LianXi1lx=newLianXi1();

13lx.run();

14}

15}

 

 

13.7小结

 

·在本章中学习了Java中的多线程,首先对线程进行了简单的介绍

,然后讲解如何定义、创建和运行多线程。

接下来还讲解了线程

的生命周期和对线程的调度。

 

1-29

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

当前位置:首页 > 经管营销 > 经济市场

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

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