
《Java程序设计案例教程》教学课件08多线程并发编程.pptx
36页模块8 多线程并发编程学习目标01掌握有关线程的基本概念02掌握通过创建Thread类的子类和通过实现Runnable接口实现多线程的方法03了解线程的状态和生命周期05了解线程同步的概念04了解线程优先级和线程调度技能目标1.能够在MyEclipse IDE中编程创建Thread类的子类实现多线程2.能够在MyEclipse IDE中编程实现Runnable接口实现多线程3.能够在MyEclipse IDE中编程实现线程同步8.1 回顾与思考多线程是Java的重要特性之一如果一个程序是单线程的,那么,任何时刻都只有一个执行点这种单线程执行方法使系统的运行效率偏低,而且,由于必须依靠中断来处理输入/输出,所以,当出现频繁输入/输出或有优先级较低的中断请求时,实时性就变得很差多线程系统可以避免这个缺点所谓多线程,就是通过系统的调用使几个具有不同功能的程序流即线程并行运行多线程可以实现同一时刻执行多个程序,使程序执行的效率变得更高浏览器就是一个典型的多线程例子在浏览器中可以一边下载文件,一边播放音频和视频,一边打印文档等多线程是实现并发的一种有效手段Java在平台上提供了对多线程的有效支持,利用语言和运行支持系统提供的复杂同步机制,同时还确保了线程的安全性。
8.2 线程的使用8.2.1 线程的基本结构1.继承Thread类(1)Thread类Thread类是负责向其他类提供线程功能的最主要的类,为了向一个类增加线程功能,可以简单地从Thread类派生一个类,并重写run()方法run()方法是线程发生的地方,它常常被称为线程体Thread类的常用构造方法及成员方法见表8-1表8-1 Thread类的常用构造方法及成员方法8.2 线程的使用8.2.1 线程的基本结构1.继承Thread类(2)用创建Thread类的子类实现多线程若用这种方法实现多线程,用户须在程序中创建Thread类的子类,并在子类中重写Thread类的run()方法来定义线程体以实现线程的具体功能,然后创建该子类的对象以创建线程,当用户创建的线程调用start()方法启动时,run()方法将被系统自动执行8.2 线程的使用8.2.1 线程的基本结构1.继承Thread类【例8-1】通过定义Thread类的子类实现多线程文件名为Example8_1.java,其代码如下其代码见P159-160运行结果如下8.2 线程的使用8.2.1 线程的基本结构2.用实现Runnable接口实现多线程Runnable接口只定义了一个run()方法,该方法是一个抽象方法,所有实现Runnable接口的类都必须具体实现这个方法,为它提供方法体并定义具体操作。
Runnable接口中的run()方法与Thread类中的run()方法一样,能被系统自动识别执行8.2 线程的使用8.2.1 线程的基本结构2.用实现Runnable接口实现多线程(1)创建实现Runnable接口的类ClassName基本语法格式如下2)创建ClassName类的对象基本语法格式如下8.2 线程的使用8.2.1 线程的基本结构2.用实现Runnable接口实现多线程(3)用带有Runnable参数的Thread类构造方法创建线程对象,对象RunnableObject作为构造方法的参数,作为新建线程的目标对象为线程提供run()方法例如,用表8-1中列出的构造方法Thread(Runnable target) 创建线程对象Thread类中除了上述构造方法带有Runnable参数外,还有下面3个构造方法也带有Runnable参数8.2 线程的使用8.2.1 线程的基本结构2.用实现Runnable接口实现多线程(4)启动定义的线程基本语法格式如下与创建Thread类的子类实现多线程相比,通过Runnable接口实现多线程方法为线程的创建提供了更大的灵活性由于Java语言不允许多重继承,如果采用创建Thread类的子类的方法创建新线程类,则该类不能再继承其他类,这就限制了程序的功能。
而后一种方法却没有这方面的顾虑8.2 线程的使用8.2.1 线程的基本结构2.用实现Runnable接口实现多线程【例8-2】通过实现Runnable接口实现多线程文件名为Example8_2.java,其代码如下其代码见P162-163运行结果如下我正在工作!我正在休息,喝咖啡!我正在工作!我正在工作!我正在工作!我正在工作!工作顺利完成!8.2 线程的使用8.2.1 线程的基本结构2.用实现Runnable接口实现多线程【例8-2】通过实现Runnable接口实现多线程文件名为Example8_2.java,其代码如下其代码见P162-163运行结果如下我正在工作!我正在休息,喝咖啡!我正在工作!我正在工作!我正在工作!我正在工作!工作顺利完成!8.2 线程的使用8.2.2 线程的状态及调度每个Java程序都有一个默认的主线程对于应用程序,主线程是main()方法执行的路径对于Applet,主线程指挥浏览器加载并执行Java小程序为实现多线程,必须在主线程中创建新的线程对象Java线程是通过Java.lang包中的Thread类来实现的可通过创建一个Thread类的对象来产生一个新的线程。
一个线程一旦产生,它就处于生命周期中的某一种状态线程的状态表示了线程正在进行的活动及能够完成的任务图8-1描述了线程的生命周期及其状态转换8.2 线程的使用8.2.2 线程的状态及调度图8-1 线程的生命周期及其状态转换从图8-1中可以看出,在一个线程从创建到消亡的整个生命周期中,总是处于下面5个状态中的某个状态8.2 线程的使用8.2.2 线程的状态及调度1.新建状态通过new关键字创建一个Thread类或其子类的线程对象时,该线程对象处于新建状态创建一个新的线程对象可以用下面的语句实现:该语句是最简单的创建线程语句,但该语句创建的线程是一个空的线程对象,系统还未对这个线程分配任何资源8.2 线程的使用8.2.2 线程的状态及调度2.就绪状态就绪状态又可称为可运行状态处于新建状态的线程可通过调用start()方法来启动start()方法产生了线程运行所需要的系统资源启动后的线程将进入线程就绪队列排队等待CPU服务,此时线程已经具备了运行的条件,一旦获得CPU等资源,线程就可以脱离创建它的主线程而独立运行3.运行状态当处于就绪状态的线程被调度并获得CPU资源时,线程就进入运行状态每个线程对象都有一个重要的run()方法,run()方法定义了该线程的操作和功能。
当线程对象被调度执行时,它将自动调用其run()方法并从第一条语句开始顺次执行8.2 线程的使用8.2.2 线程的状态及调度4.阻塞状态阻塞状态又称为不可运行状态当发生下列情况之一时,线程就进入阻塞状态1)等待输入/输出操作完成2)线程调用wait()方法等待一个条件变量3)调用了该线程的sleep()休眠方法4)调用了suspend()悬挂方法8.2 线程的使用8.2.2 线程的状态及调度5.消亡状态消亡状态又称死亡状态当调用run()方法结束后,线程就进入消亡状态,这是线程的正常消亡另外,线程还可能被提前强制性消亡不管何种情况,处于消亡状态的线程都不具有继续运行的能力8.2 线程的使用8.2.3 线程的控制多线程编程对于许多程序员来说是一件棘手的事,特别是当运行的线程数量很多时,控制这些线程相当困难Java提供了线程组(java.lang.ThreadGroup类)来控制线程,使同时改变大量线程的运行状态成为可能在创建线程之前,可以创建一个ThreadGroup对象,下面的代码片段是创建一个线程组,并在其中加入两个线程的例子当线程组被创建以后,每一个线程被创建并传递给ThreadGroup对象,成为线程组中的成员。
8.2 线程的使用8.2.3 线程的控制【例8-3】用线程组控制线程文件名为Example8_3.java,其代码如下其代码见P166-167某次运行的结果如下其代码见P167-1688.3 线程的管理8.3.1 优先级Java中每个线程都有一个优先级,线程调度器根据优先级的高低决定当前哪个线程先执行优先级高的线程先执行,优先级低的线程后执行这样就可以为任务较紧急的重要线程设置较高的优先级,在就绪队列中排在前面;反之,则设置较低的优先级,等到优先级高的线程执行结束后才被执行,让情况紧急的线程首先得到调度但若线程优先级相同,则遵循队列的“先进先出”原则,即先到的线程先获得CPU资源执行8.3 线程的管理8.3.1 优先级在Java中,线程的优先级是用数字来表示的,其取值范围为110另外,Thread类提供3个有关线程优先级的静态属性,见表8-2表8-2 Thread类定义的静态属性8.3 线程的管理8.3.1 优先级当一个在就绪队列中排队的线程获得CPU资源而转入运行状态后,则称这个线程被调度线程的调度遵循优先级基础上的抢先策略抢先策略是指以下几个方面1)若一个比当前活动线程优先级更高的线程进入就绪状态,则停止当前活动线程的执行,当前活动线程转入阻塞状态,插入就绪队列中重新等待调度,而优先级高的线程转入运行状态,成为活动线程。
2)若一个比当前活动线程优先级低的线程进入就绪状态,则当前活动线程不停止继续执行,刚进入就绪状态的线程在就绪队列中等待调度3)若一个与当前活动线程优先级相同的线程进入就绪状态,则遵循“先到先服务”的原则8.3 线程的管理8.3.2 同步线程同步的基本思想是协调各线程对共享资源的使用,避免多个线程在某段时间内对同一资源的访问,这个资源可以是数据、代码、设备等Java通过关键字synchronized来表明被同步的资源,即给资源加“锁”,这个“锁”被称为信号锁当某个资源被synchronized关键字修饰时,系统在运行时都会分配给它一个信号锁,表明该资源在同一时刻只能由一个线程访问但是,处理线程同步仅仅定义信号锁是不够的,Java中专门设计了以下3个方法与信号锁配合使用8.3 线程的管理8.3.2 同步01public final void wait()(等待方法)该方法是一个最终方法,不能被重写,只能在同步方法中被调用执行该方法会将当前正在运行的线程悬挂,释放所占用的资源,从运行状态转入阻塞状态,进入wait队列等待被唤醒02 public final void notify()(唤醒方法)该方法也是一个最终方法,不能被重写,只能在同步方法中被调用。
执行该方法将唤醒wait队列中优先级最高的一个线程,占有资源运行8.3 线程的管理8.3.2 同步03public final void notifyAll()(唤醒所有方法)除了notify()方法以外,Java还设计了notifyAll()方法它的作用与notify()方法相似,不过它是唤醒wait队列中所有的线程wait()、notify()和notifyAll() 3种方法必须在同步方法中被调用因此,它们只能出现在synchronized作用的范围内8.3 线程的管理8.3.2 同步在Java中,用synchronized表示同步有两种形式:同步代码块和同步方法1)同步代码块其基本语法格式如下其中,someObject代表当前对象当程序执行到synchronized设定的同步化区块时,锁定当前对象,单独执行该同步化区块8.3 线程的管理8.3.2 同步在Java中,用synchronized表示同步有两种形式:同步代码块和同步方法2)同步方法其基本语法格式如下在调用同步方法的线程执行结束前,其他调用该同步方法的线程都会被阻塞8.3 线程的管理8.3.2 同步【例8-4】生产者-消费者模型演示。
文件名为Example8_4.java,其代码如下其代码见。












