java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf ·...

40
Java 软件设计基础 Java Java 线程机制 线程机制 PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Upload: dinhnga

Post on 18-Feb-2018

258 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

Java 软件设计基础

JavaJava线程机制线程机制

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 2: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

1.Java中的多线程机制

•• 基本概念基本概念– 程序

• 程序是为完成特定任务、用某种语言编写的一组指令的集合,指一段静态的代码。

– 进程• 进程是程序的一次执行过程,是系统进行调度和资源分配的一个独立单位,每个进程都有自己独立的一块内存空间、一组系统资源。在进程概念中,每个进程的内部数据和状态都是完全独立的。• 计算机上同时运行多个程序叫进程并发。操作系统按照一定的策略调度各个进程执行,以便最大限度的利用计算机的各种资源。

– 线程的基本概念• 线程(Thread)是进程中某个单个顺序的控制流,也被称为轻量进程(Lightweight Process)。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 3: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

• 线程是进程中的实体。一个进程可以有多个线程,一个线程必须有一个父进程。• 线程不拥有系统资源,只有运行必需的一些数据结构;它与父进程的其他线程共享该进程拥有的全部资源。

– 多线程• 多线程指在单个程序中可以同时运行多个不同的线程,执行不同的任务,这是实现并发机制的一种有效手段。• 操作系统使用分时管理各个进程,按时间片轮流执行每个进程。Java的多线程就是在操作系统每次分时给Java程序一个时间片的CPU时间内,在若干独立的可控制的线程之间切换。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 4: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

多个线程运行在多个CPU上

多个线程共享单个CPU

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 5: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• 线程的生命周期线程的生命周期– 同进程一样,线程也有从创建、运行到消亡的过程,这称为线程的生命周期。

– 创建状态• 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于创建状态,这是线程已被创建但未开始执行的特殊状态。处于创建状态的线程是一个空的线程对象,系统不为它分配资源,但有自己的内存空间。• 创建状态的线程通过调用start()方法进入就绪状态。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 6: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 就绪状态• 处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,因而将进入线程队列,等待系统为其分配CPU。• 线程根据自身优先级进入等待队列的相应位置。• 一旦获得CPU,线程就进入运行状态,并自动调用自己的run()方法。

– 运行状态• 进入运行状态的线程顺序执行自己run()方法中的代码,直到调用其他方法而终止,或等待某资源而阻塞,或完成任务而死亡。• 运行状态表示线程拥有了对处理器的控制权,其代码正在运行,除非运行过程的控制权被另一优先级更高的线程抢占,否则这个线程将一直持续到运行完毕。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 7: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 阻塞状态• 处于运行状态的线程在某些情况下让出CPU并暂时终止自己的运行,进入阻塞状态。

– 执行了sleep()方法;– 等待I/O设备等资源

• 在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时才能转入就绪状态。

– 睡眠时间已到– 等待的I/O设备空闲

• 当再次获得CPU时,从原来终止的位置开始继续运行。• 注意:若线程正在等待某个条件,那么相关联的另一个线程必须通过调用notify()或notifyAll()方法告诉正在等待的线程条件发生了变化。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 8: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 死亡状态• 这是线程生命周期中的最后一个阶段,表示线程已退出运行状态,并且不再进入就绪队伍。当线程的run()方法结束或由于其他原因被终止后,线程就进入消亡状态。线程的终止分两种情况:

– 自然死亡»即从线程的run()方法正常退出;

– 被强制性终止»使用Thread类中的destroy()方法或stop()方法终止»Java系统不赞成使用destroy()方法或stop()方法终止线程,因为若使用不当,可能会导致程序的死锁。

– 总结

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 9: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• 多线程的实现方法多线程的实现方法– 多线程有两种实现方法:

• 通过继承创建Thread类的子类来实现;• 利用Runnable接口来实现。

– 当程序作为一个应用程序运行时,Java解释器为main方法启动一个线程。当程序作为一个applet运行时,Web浏览器启动一个线程来运行applet。– 在程序中还可以创建附加的线程以执行并发的任务。– Java中每个任务都是一个Runnable接口的实例,也称为可运行对象。

线程本质上就是实现任务的对象。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 10: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• 创建任务和线程创建任务和线程– 创建任务

• 声明一个任务类,它必须实现Runnable接口。– Runnable接口只包含一个run方法,需要实现这个方法来告诉系统线程是如何运行的。

• 一旦声明了一个任务类,就可以利用其构造方法创建一个任务。

– 在线程中执行任务• 根据任务对象创建线程;• 调用start方法告诉JVM该线程准备运行。• JVM通过调用任务中的run方法执行任务。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 11: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 线程的一些常用方法:• start()方法

– 线程调用该方法启动一个线程,使之从新建状态进入就绪队列排队,一旦轮到它来使用CPU资源时,就可以脱离创建它的主线程独立开始自己的生命周期。

• run()方法– 线程的所有活动都是通过线程体run()方法来定义并实现它被调用以后所执行的操作。– Runnable接口中的run()方法是由系统自动调用而用户程序不得引用的方法。

• sleep()方法– 使线程睡眠一段时间,单位为毫秒。– 正在运行的高优先级线程可以在它的run()方法体中调用sleep()方法来使自己放弃处理器资源而休眠一段时间,长短由sleep()方法的参数决定。– 如果线程在休眠时被打断,JVM就抛出InterruptedException异常,因此必须在try-catch语句块中调用sleep()方法。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 12: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

• isAlive()方法– 测试线程是否处于活动状态。

• interrupt()方法– 用来唤醒休眠的线程。系统可以通过一定方式让休眠的线程分别调用interrupt()方法,导致休眠的线程发生InterruptedException异常,从而结束休眠,重新排队等待CPU资源。

• yield()方法– 把线程移到队列的尾部。

• destroy()方法– 结束线程生命周期但不做清理工作。

• stop()方法– 结束线程生命周期并执行清理工作。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 13: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• 线程调度与优先级线程调度与优先级– 在CPU上以某种次序执行多个线程被称为调度。– 线程调度器

• 为了控制线程的运行策略,Java定义了线程调度器来监控系统中处于就绪状态的所有线程。• 线程调度器采用“抢占式”策略来按照线程的优先级决定哪个线程获取处理器投入运行。

– 优先级• Java将线程的优先级分为10个等级,用1~10表示,数字越大表示优先级越高。• Thread类中定义了三个成员变量:

– MIN_PRIORITY:最低级(1级)

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 14: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– MAX_PRIORITY:最高级(10级)– NORM_PRIORITY:普通优先级(5级)

• 当线程对象被创建时,默认优先级是5级。• 线程可以通过调用setPriority方法改变优先级。

– 抢占式调度• 时间片方式• 独占方式

•• 实例实例

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 15: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 注意:• 并不是在所有系统中运行Java程序时都采用时间片策略调度线程。一个线程在空闲时应该主动放弃CPU,以使其他同优先级和低优先级的线程得到执行。• 在Java中比较特殊的一类线程是被称作守护(Daemon)线程的低级别线程,这种线程具有最低的优先级,用于为系统中的其他对象和线程提供服务。可以通过setDaemon方法将一个线程设置为守护线程。• JVM中的系统资源回收线程就是典型的守护线程。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 16: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

2.多线程的创建与实现

•• ThreadThread类类– 在默认情况下run()是空的,必须根据需要重新设计线程的run方法,再使用start方法启动线程,将执行权转交到run。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 17: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 过程• 使应用程序继承Thread类,并且在该类的run方法中实现并发性处理过程。

– 优点• 可以在子类中增加新的成员变量,使线程具有某种属性;• 可以在子类中增加新的方法,是线程具有某种功能;

– 注意• run()方法必须进行重写,把要在多个线程中并行处理的代码放到这个方法中。• 虽然run()方法实现了多个线程的并行处理,但不能直接调用run()方法,而是通过start()方法来调用。在调用start()的时候,start方法会首先进行与多线程相关的初始化,然后再自动调用run()方法。

– 实例

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 18: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and TechnologyPDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 19: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• RunnableRunnable接口接口– Runnable接口中定义的run方法

– 还可以通过向Thread()类构造方法传递一个实现了Runnable接口的对象来创建进程。Thread类有多个构造方法,其中可以接受Runnable参数的构造方法有以下两个:

– 通过Runnable接口实现线程的步骤:• 定义实现Runnable接口的类

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 20: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

• 创建实现Runnable接口的类对象• 创建线程对象• 启动线程

– 实现接口Runnable的类仍然可以继承其他父类,例如:

– 实现Runnable接口的多线程应用程序框架:

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 21: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 实例

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 22: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

public class RunnableEx{public static void main(String args[]){

Target first,second;first=new Target("First thread ");second=new Target("Second thread ");Thread one,two;one=new Thread(first);two=new Thread(second);one.start();two.start();

}}class Target implements Runnable{

String s;public Target(String s){

this.s=s;System.out.println(s+"Now building");

}public void run(){

System.out.println(s+"Now running");try{

Thread.sleep(1000);}catch(InterruptedException e){System.out.println(e.toString());}System.out.println(s+"Now over");

}}

First thread Now buildingSecond thread Now buildingFirst thread Now runningSecond thread Now runningSecond thread Now overFirst thread Now over

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 23: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• 继承继承ThreadThread类与实现类与实现RunnableRunnable接口的对比接口的对比– 使用Runnable接口:可以将CPU、代码和数据分开,形成清晰的风格;可以从其他类继承,适用面广,保持程序风格的一致性;– 直接继承Thread类:编写简单,可以直接操纵线程,但不能再从其他类继承。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 24: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and TechnologyPDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 25: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

也可以将以上模式改写如下:声明一个Thread类的子类,并实现Runnable接口。然后在客户端程序中用

这个类生成一个对象,并调用它的start方法来启动线程。

不推荐使用以上模式,因为它将任务和任务运行的机制混在一起。将任务从

线程中分离出来是比较好的设计。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 26: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

3.线程的同步与死锁

•• 线程的等待线程的等待– Java程序中的线程并发运行将共同争夺CPU资源,哪个线程抢夺到CPU资源后就开始运行。线程之间往往会有冲突,为规避这种现象,引入线程等待的概念。– 由于线程的调度执行是按照优先级高低的顺序进行,因此可使用sleep方法让高级线程休眠一段时间使得低优先级线程有机会执行。– 暂停线程执行的方法

• sleep()方法• suspend()方法和resume()方法

– 线程的暂停和恢复可通过调用suspend()方法使线程暂时由可运行状态切换到不可运行状态。若此线程想再回到可运行状态,必须由其他线程调用resume()方法来实现。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 27: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

• join()方法– 表示当前线程等待调用该方法的线程结束后再恢复执行。– 一个线程A在占有CPU资源期间,可以让其他线程B调用join()方法与本线程联合,即“B.join();”,这样称A在运行期间联合了B。– 如果线程A在占有CPU资源期间一旦联合B线程,那么A线程将立即中断运行,一直等到它联合的线程B执行完毕,A线程才再重新排队等待CPU资源。– 如果A准备联合的线程B已经结束,则“B.join();”语句将不起任何作用。

由于普遍认为上述两种方法和stop方法具有内在的不安全因素,在Java2中不提倡这些方法。为了替代stop()的使用,可以通过给Thread变量赋值为null,指出要停止它的运行。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 28: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 实例

class ThreadEx2 extends Thread{String s;int m,i=0;ThreadEx2(String s){

this.s=s;}public void run(){

try{for(i=0;i<4;i++){

sleep((int)(500*Math.random()));System.out.println(s);

}System.out.println(s+"finished");

}catch(InterruptedException e){return;}}public static void main(String args[]){

ThreadEx2 a=new ThreadEx2("a ");ThreadEx2 b=new ThreadEx2("b ");a.start();b.start();System.out.println("main is finished");

}}

main is finisheda b a b a b a a finishedb b finished

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 29: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

public class ThreadJoin{public static void main(String argsp[]){

TJoin a = new TJoin();a.threadA.start(); a.threadB.start();

}}class TJoin implements Runnable{

Thread threadA,threadB;String content[]={"各就位","预备","跑"};public TJoin(){

threadA=new Thread(this); threadB=new Thread(this);threadB.setName("发令员");

}public void run(){

if(Thread.currentThread()==threadA){System.out.println("等待"+threadB.getName()+"发令");try{threadB.join();}catch(InterruptedException e){return;}System.out.println("开始跑!");

}else if(Thread.currentThread()==threadB){

System.out.println(threadB.getName()+"发令:");for(int i=0;i<content.length;i++){

System.out.println(content[i]);try{threadB.sleep(1000);}catch(InterruptedException e){return;}

}}

}}

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 30: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• 线程的同步线程的同步– 线程同步概念的提出

• Java应用程序的多个线程共享同一进程的数据资源,多个用户线程在并发运行过程中可能会同时访问具有敏感性的内容。一个线程需要修改该内容时,另一个线程也要修改该内容,系统需要对以上情况作出使当的处理。• Java中定义了线程同步的概念,用来实现共享数据的一致性维护。• 线程同步的使用主要是保证一个进程中多个线程的协调工作,使得线程安全的共享数据,转换和控制线程的执行,保证内存的一致性。

– 实现同步的方法• 在共享内存变量的方法前加上synchronized修饰符,在程序的运行中,如果某一线程调用经synchronized修饰的方法,在该线程结束此方法之前,其他所有线程都不能运行该方法,只有等待到该方法被释放为止。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 31: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

• 在线程同步中要做的第一件事就是要把修改数据的方法用关键字synchronized来修饰。

– synchronized修饰符的使用• 修饰方法

• 放在对象前面限制一段代码的执行

• 作为类修饰符,表明该类中的方法都是synchronized的

– 实例:

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 32: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

public class BankEx{public static void main(String args[]) throws InterruptedException{

BankSave bs=new BankSave();Operator o1=new Operator(bs,"丈夫");Operator o2=new Operator(bs,"妻子");Thread t1=new Thread(o1);Thread t2=new Thread(o2);t1.start(); t2.start();Thread.currentThread().sleep(500);

}}class BankSave{

private static int money=1000;public void add(int i){

money=money+i;System.out.println("丈夫存入"+i+"元,余额"+money+"元");

}public void get(int i){

if(i<=money){money=money-i;System.out.println("妻子取走"+i+"元,余额"+money+"元");

}else System.out.println("余额不足");

}public int showMoney(){return money;}

}

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 33: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

丈夫存入1000元,余额2000元丈夫存入1000元,余额3000元丈夫存入1000元,余额4000元丈夫存入1000元,余额5000元丈夫存入1000元,余额6000元妻子取走1000元,余额5000元妻子取走1000元,余额4000元妻子取走1000元,余额3000元妻子取走1000元,余额2000元妻子取走1000元,余额1000元

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 34: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• 线程的死锁线程的死锁– 当两个或多个线程等待一个不可能满足的条件时会发生死锁。– 死锁是发生在线程间相互阻塞的现象,允许多个线程并发访问共享资源时,必须提供同步机制,然而如果对这种机制使用不当,可能会出现线程永远被阻塞的现象。– 例如两个线程分别等待对方各自占有的一个资源,就会产生死锁。– Java本身既不能预防死锁,也不能发现死锁,只能靠程序设计上的谨慎来避免。

•• 线程的调度线程的调度– 线程调度方法

• Java的线程同步运行会竞争资源,但是它们的任务间存在一定的关联,这就需要调度。• Java提供了三个方法实现线程的调度和通信:

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 35: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– wait():等待方法。可让当前线程放弃监视器并进入睡眠状态,直到其他线程进入同一监视器并调用notify()方法为止。– notify():唤醒方法。可唤醒同一对象监视器中调用了wait()方法的第一个线程,并把它移入锁申请队列。– notifyAll():唤醒所有线程的方法。唤醒同一对象监视器中调用了wait()方法的所有线程,具有最高优先级的线程首先被唤醒。

– 线程的调度规则• 如果两个或两个以上的线程都修改一个对象,那么把执行修改的方法定义为synchronized的,若对象更新影响到只读方法,那么只读方法应该也定义为同步的;• 如果一个线程必须等待一个对象状态发生变化,那么它应该在对象内部等待,而不是在外部等待,它可以调用一个被同步的方法,并让这个方法调用wait();• 每当一个方法改变某个对象的状态时,它应该调用notifyAll()方法,这给等待队列的线程提供机会来看一看执行环境是否已发生变化。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 36: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

• wait()、notify()、notifyAll()方法属于Object类而非Thread类,在使用时要仔细检查是否每个wait()方法都有相应的notify()或notifyAll()方法且作用于相同的对象。• 对于多个线程同时等待某个条件的情况,当条件满足时应该使用notifyAll()方法唤醒。否则某些线程会一直等待下去导致死锁。更为保险的做法是,任何情况下都使用notifyAll()方法唤醒等待的线程。• 应当区分由于得不到锁而等待的线程和由于调用了wait()方法而等待的线程。• wait()和notify()只能在一个同步的方法或代码块内部调用,若在一个不同步的方法内调用,运行时将产生非法监视器状态异常(IllegalMonitorStateException)。• Java中的每个类都有一个主线程。要执行一个程序,那么这个类当中一定要有main方法,main方法就是Java类中的主线程。

– 实例:模拟三人买票• 说明:

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 37: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

– 售货员最初只有一张五元钱,电影票五元一张。– 排队的三人中,A有一张二十元人民币,B有一张十元人民币,C有一张五元人民币。如果给售票员的钱不是零钱,而售票员又没有零钱找,那么此人必须等待,并允许后面的人买票,以便售票员获得零钱。

• 分析– A/B/C三人 买票就像三个线程,每个线程必须满足以上条件(售票员有足够的找零或者提供售票员的是五元零钱)才能执行;– A/B/C三人同时只能有一人向售票员买票;不满足条件时需要等待。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 38: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and TechnologyPDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 39: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

else if(receiveMoney==20){while(five<1||ten<1){

try{System.out.println(Thread.currentThread().getName()+" 请您一旁等待");wait();System.out.println(Thread.currentThread().getName()+"结束等待");

}catch(InterruptedException e){return;

}}five--;ten--;twenty++;

System.out.println(Thread.currentThread().getName()+"给我二十元钱,售票一张,找零十五元");}notifyAll();

}}public class TicketEx{

public static void main(String args[]){Cinema a = new Cinema();a.A.start();a.B.start();a.C.start();

}}

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Page 40: Java线程机制 - staff.ustc.edu.cnstaff.ustc.edu.cn/~renkx/chp10.pdf · 立单位,每个进程都有自己独立的一块内存 ... • 操作系统使用分时管理各个进程,按时间片轮流

School of Computer Science and Technology

•• 思考题:等待电梯的程序思考题:等待电梯的程序– 假设电梯停靠楼层为1~8,8个楼层都有可能有人要求搭乘电梯去往目的楼层。– 电梯控制台在同一时刻只可能对一个方向(上、下)作出响应,并到达得到响应的楼层。– 再复杂一些,在响应后的某个行进方向上如果有楼层的搭乘要求符合该方向,则“顺便”完成该要求。

• 如当前在8楼的电梯响应1楼的要求向下行进,如果此时4楼的向下键被按下,则在4楼停靠,顺路搭载4楼的用户。

– 电梯的运行时间上的延迟可以用sleep方法完成,未得到响应的用户则通过wait来等待。

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn