Java程序设计pp8教学课件.ppt

上传人:春哥&#****71; 文档编号:90588483 上传时间:2023-05-16 格式:PPT 页数:55 大小:5.03MB
返回 下载 相关 举报
Java程序设计pp8教学课件.ppt_第1页
第1页 / 共55页
Java程序设计pp8教学课件.ppt_第2页
第2页 / 共55页
点击查看更多>>
资源描述

《Java程序设计pp8教学课件.ppt》由会员分享,可在线阅读,更多相关《Java程序设计pp8教学课件.ppt(55页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、Java程序设计pp8教学课件Java程序设计目录目录第 8 章多线程编程8.1 线程的概念8.2 创建多线程8.3 线程的控制与调度8.4 线程的同步机制8.1 线程的概念提到线程,首先必须知道什么是进程(提到线程,首先必须知道什么是进程(process)。进)。进程和线程是两个不同的概念,但又彼此相关。简单来说,一程和线程是两个不同的概念,但又彼此相关。简单来说,一个程序至少有一个进程,一个进程至少有一个线程。而线程个程序至少有一个进程,一个进程至少有一个线程。而线程的划分尺度小于进程,使得多线程程序的并发性高。另外,的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有

2、独立的内存单元,而多个线程共享内进程在执行过程中拥有独立的内存单元,而多个线程共享内存,这极大地提高了程序的运行效率。存,这极大地提高了程序的运行效率。8.1 线程的概念8.1.1 进程与线程在操作系统中有这样的定义,即在操作系统中有这样的定义,即程序是数据描述与操作代码的集合。程序是数据描述与操作代码的集合。进程是程序的一次执行过程,是系统进程是程序的一次执行过程,是系统运行程序的基本单位。线程的概念是运行程序的基本单位。线程的概念是基于进程的。基于进程的。8.1 线程的概念1)进程进程是一块包含某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。进程还可以定义为具有一定独立功

3、能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。在运行于32位处理器上的Windows操作系统中,可将一个进程视为一段大小为4 GB(232 B)的线性内存空间,它起始于0 x00000000,结束于0 xFFFFFFFF。这段内存空间不能被其他进程访问,所以称为该进程的私有空间。这段空间被平分为两块,其中2 GB为系统所有,剩下2 GB为用户所有。Windows是按需为每个进程分配内存的,4 GB是32位系统中一个进程所占空间的上限。8.1 线程的概念进程具有以下特点。(3)进程是程序的一次执行过程,是系统运行程序的基本单位。(2)创建并执行一个进程的系

4、统开销是比较大的。(1)一个进程就是一个执行中的程序,每个进程都有自己独立的一块内存空间和一组系统资源。8.1 线程的概念程序的运行结果如图8-1所示。8.1 线程的概念2)线程当运行一个进程时,程序内部的代码都是按顺序先后执行的。如果能够将一个进程划分成更小的运行单位,则程序中一些彼此相对独立的代码段可以重叠运行,从而获得更高的执行效率。实际上,大多数程序都包含一些彼此相对独立的代码段。线程就为这种重叠执行提供了可行的方法。线程是比进程更小的运行单位,一个线程包含以下内容。(1)一个指向当前被执行指令的指令指针。(2)一个栈。(3)一个寄存器值的集合,定义了一部分描述正在执行线程的处理器状态

5、的值。(4)一个私有的数据区。8.1 线程的概念一个进程可以包含多个线程。与进程不同,线程是一种特殊的多任务方式,也可以这样说,线程是指程序的运行流程,多线程是一种机制,在这种机制下,可以同时运行多个程序块,从而使程序运行的效率变得更高,也可克服传统程序语言无法解决的问题。例如,有些包含循环的线程可能要使用比较长的一段时间来运行,此时便可让另一个线程来进行其他处理。当一个程序支持多线程时,可以运行两个或更多的由同一个程序启动的任务,这样一个程序可以使得多个活动程序同时发生。如果一个程序需要打印一个文档或在后台运行,同时又需要定期与用户进行数据交互,那么就可以采用多线程来实现。8.1 线程的概念

6、3)进程和线程的比较进程和线程的主要差别在于它们是不同的操作系统资进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是一个在保护模式下不会对其他进程产生影响,而线程只是一个进程中的不同执行路径。进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间;多进程的程序要比多线程的程序健壮,但在的地址空间;多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要低一些。但对于一些进

7、程切换时,耗费资源较大,效率要低一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。线程,不能用进程。8.1 线程的概念8.1.2 线程的生命周期与态同进程一样,一个线程也有从创建、运行到消亡的过程,这称为线程的生命周期。线程的状态表明了线程处于生命周期的哪个阶段。Java中的线程有创建、就绪、运行、阻塞、死亡5种状态。通过控制与调度可使线程在这几种状态间相互转化,如图8-2所示。8.1 线程的概念1)创建状态使用使用new运算符创建一个线程之后,该线程仅运算符创建一个线程之后,该线程仅仅是一个空对象,系统没有为

8、它分配资源,称该线仅是一个空对象,系统没有为它分配资源,称该线程处于创建状态。程处于创建状态。当一个线程被创建后,只能在相应的状态进行当一个线程被创建后,只能在相应的状态进行允许的操作,若操作不当会引起非法状态异常允许的操作,若操作不当会引起非法状态异常(illeagal thread state exception)。)。8.1 线程的概念2)就绪状态使用start()方法启动一个线程后,系统为该线程分配了除CPU之外的所需资源,使该线程处于就绪(runnable)状态。此时说明该线程已经进入线程队列并排队等待运行,但实际上并未运行。8.1 线程的概念3)运行状态 Java运行系统通过调度选

9、中一个可运行的线程,使其占有CPU并转为运行状态(running)。此时,系统真正执行线程的run()方法。对于一个CPU来讲,要在同一时刻运行所有线程是不可能的,所以Java运行系统必须通过调度来保证处于就绪状态的线程能够共享处理器。8.1 线程的概念4)阻塞状态一个正在运行的线程由于某种原因不能继续运行时,将进入阻塞状态(blocked),也称为不可运行状态(not runnable)。一般地,阻塞状态可分为等待阻塞、互斥阻塞和其他阻塞3种。处于阻塞状态的线程是不能运行的,即使处理器空闲也不能运行。当引起阻塞的原因消除时,线程转入就绪状态,重新进入线程队列,排队等候运行,再次运行时将从原来

10、中止处继续运行。导致线程处于阻塞状态的原因有多种,如输入/输出、等待消息、睡眠、锁定等。8.1 线程的概念5)死亡状态线程结束后是死亡状态(dead)。导致线程死亡有两种原因:自然撤销或被停止。自然撤销指线程的正常退出。当run()方法结束后,该线程就自然撤销。当一个应用程序因故停止运行时,系统将中止该程序正在运行的所有线程。例如,当用户从一个主页切换到另一个主页时,原主页中正在运行的所有线程都会终止。在Java中,可以用isAlive()方法测试线程是否已启动。如果isAlive()返回false,则表示该线程是新创建的或已经被终止;如果返回true,则表示该线程已启动并没有终止,是就绪状态

11、、运行状态或阻塞状态之一,但不能确定是这3种状态中的哪一种。8.2 创建多线程8.2.1 通过继承Thread类实现多线程Thread类将Runnable接口中的run()方法实现为空方法,并定义了许多用于创建和控制线程的方法。Thread类的声明格式如下。Thread类的构造方法如下。(1)public Thread()。(2)public Thread(String name)。(3)public Thread(Runnable target)。(4)public Thread(Runnable target,String name)。(5)public Thread(ThreadGrou

12、p group,Runnable target)。(6)public Thread(ThreadGroup group,String name)。8.2 创建多线程下面是由下面是由Thread类定义的一些方法。类定义的一些方法。(1)Thread current(Thread)。(2)int activeCount()。()。(3)int enumerate(Thread tArray)。(4)String getName()。()。(5)void setName(String name)。(6)void start()。()。(7)boolean isAlive()。()。(8)Thread

13、Group getThreadGroup()。()。(9)String toString()。()。【例8-2】通过继承Thread类实现多线程。在记事本中输入以下代码。public class ThreadDemo_threadpublic static void main(String args)new TestThread().start();for(int i=0;i5;i+)/主线程输出tryThread.sleep(50);catch(InterruptedException e)e.printStackTrace();8.2 创建多线程程序将输出主线程和子线程交替运行的情况。程序

14、的运行结果如图8-3所示。8.2 创建多线程8.2.2 通过Runnable接口实现多线程在实际开发中,一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。Runnable接口中只声明了一个run()方法,如下所示。pubic void run()run()方法是线程执行的起点,即在创建并启动一个线程后,系统自动调用run()方法。一个线程对象必须实现run()方法,以完成线程的所有活动。已实现的run()方法称为该对象的线程体。任何实现Runnable接口的对象都可以作为一个线程的目标对象。但是使用 Runnable 定义的子类中没有start()方法,只有Thread

15、类中才有。在Thread类中有下面的构造方法。public Thread(Runnable target)8.2 创建多线程【例8-3】通过Runnable接口实现多线程。在记事本中输入以下代码。public class ThreadDemo_runnable public static void main(String args)TestThread t=new TestThread();new Thread(t).start();/循环输出for(int i=0;i5;i+)System.out.println(main is running);tryThread.sleep(50);ca

16、tch(InterruptedException e)8.2 创建多线程程序将输出主线程和子线程的交替执行情况。程序的运行结果如图8-3所示。8.2 创建多线程8.2.3 两种实现方法的比较8.2 创建多线程在程序开发中只要是多线程,一般都以实现Runnable接口为主。因为使用Runnable接口实现多线程有以下好处。(1)避免继承的局限,一个类可以继承多个接口。(2)适合于资源共享。下面以使用两种方法实现同一个多线程卖票程序为例,比较两种实现方法的不同。程序的运行结果如图8-4所示。8.2 创建多线程程序的运行结果如图8-5所示。8.2 创建多线程8.3 线程的控制与调度8.3.1 线程的

17、调度同一时刻如果有多个线程处于可运行状态,则它们需要排队等候CPU资源。此时每个线程自动获得一个线程的优先级(priority),优先级的高低反映线程的重要或紧急程度。可运行状态的线程按照优先级排队,线程调度在优先级基础上依据先到先服务原则。线程调度管理器负责线程排队和CPU在线程之间的分配,并由线程调度算法进行调度。当线程调度管理器选中某个线程时,该线程获得CPU资源而进入运行状态。线程调度是先占式调度,即如果在当前线程执行过程中,一个优先级别更高的线程进入可运行状态,则这个线程立即被调度执行。先占式调度分为独占方式和分时方式。(1)在独占方式下,当前线程将一直执行下去,直到执行完毕或由于某

18、种原因主动放弃CPU,或CPU被一个优先级别更高的线程抢占。(2)在分时方式下,当前运行线程获得一个时间片,时间片结束时,即使线程没有完成也要让出CPU,进入可运行状态,等待下一个时间片的调度。系统则选中其他可运行状态的线程执行。分时方式的系统使每个线程工作若干步,从而实现多线程同时运行。8.3 线程的控制与调度8.3.2 线程的优先级线程的优先级用数字110表示,1表示优先级最高,默认值为5。每一个优先级的值对应一个Thread类的公用静态常量,如下所示。public static final int NORM_PRIORRTY=5;public static final int MIN_P

19、RIORITY=1;public static final int MAX_PRIORITY=10;下面是与线程优先级有关的方法。(1)public final int getPriority()。该方法获得线程的优先级。(2)public final int setPriority(int newPriority)。该方法设定线程的优先级。8.3 线程的控制与调度8.3.3 改变线程的状态1)线程睡眠方法sleep()sleep()方法的定义如下。public static void sleep(long millis)throws InterruptedException该方法使当前执行的

20、线程转入睡眠状态(也是一种阻塞状态),睡眠时间为参数中指定的millis(毫秒)。如果在程序中对某个线程执行了sleep()方法,那么该线程将睡眠(停止执行)若干毫秒,该线程的状态由运行转入就绪。只有睡眠时间过后,线程才会再进入运行状态。下面的例子演示了如何使用sleep()方法改变线程的状态。在程序中使用了线程组来创建两组线程,同时使用了Thread类的其他方法获得当前线程组和活动线程的名称与状态。8.3 线程的控制与调度程序的运行结果如图8-6 所示。8.3 线程的控制与调度2)暂停线程方法yield()yield()方法的定义如下。public static void yield()该方

21、法使当前正在执行的线程暂时停止运行。具体地说,yield()方法暂停当前线程的执行,允许其他线程执行。暂停的线程仍处于运行状态,并不转为阻塞状态。此时,系统选择其他同优先级线程执行,若无其他同优先级线程,则选中该线程继续执行。yield()方法的优点是保证有工作时不会让CPU闲置。该方法主要用于编写多个合作线程,也适用于强制线程间的合作。8.3 线程的控制与调度3)连接线程方法join()join()方法的定义如下。pubic final void join()(long millis)throws InterruptedException该方法使当前线程暂停执行参数中指定的millis(毫秒

22、),等待调用该方法的线程结束后再继续执行本线程。一般地,线程等待调用该方法的线程结束,或最多等待mills(毫秒)后,再继续执行本线程。如果需要在一个线程中等待直到另一个线程消失,可以调用join()方法。如果当前线程被另一个线程中断,join()方法会抛出InterruptedException异常。8.3 线程的控制与调度4)中断线程方法interrupt()interrupt()方法的定义如下。pubic void interrupt()与interrupt()方法相关的方法还有isInterrupt()和interrupted(),它们的定义分别如下。(1)public boolean

23、 isInterrupted()。该方法判断当前线程是否被中断。如果被中断,则返回true,否则返回false。(2)public static boolean interrupted()。该方法的用法同isInterrupted()方法。8.3 线程的控制与调度5)线程状态的转换8.3 线程的控制与调度(1)创建就绪。(2)就绪运行。(3)运行死亡。(4)运行阻塞。(5)阻塞就绪。8.3.4 获取和设置线程的名称线程的名称一般在启动线程前设置,如果程序没有为线程指定名称,系统会自动为线程分配名称。Java允许为已经运行的线程设置名称。在Thread类中,可以通过getName()方法获取线程

24、的名称,并通过setName()方法设置线程的名称。8.3 线程的控制与调度程序将获取当前处于运行状态的线程的名称,然后输出。程序的运行结果如图8-7所示。8.3 线程的控制与调度【例8-8】设置线程的名称。在记事本中输入以下代码。public class SetNameThreadDemo extends Threadpublic void run()for(int i=0;i3;i+)tryThread.sleep(60);catch(InterruptedException e)e.printStackTrace();printMsg();8.3 线程的控制与调度程序的运行结果如图8-8

25、所示。8.3 线程的控制与调度8.3.5 判断线程是否启动 在创建线程后,可以在程在创建线程后,可以在程序中判断线程是否已经启动。序中判断线程是否已经启动。Java是通过是通过Thread类的类的 isAlive()()方法判断线程是方法判断线程是否启动的。否启动的。8.3 线程的控制与调度程序的运行结果如图8-9所示。8.3 线程的控制与调度8.4 线程的同步机制8.4.1 共享数据的线程互斥锁定1)线程间的数据共享对于银行账户,银行和客户可以同时对其进行操作,所以分别设计银行账户类 Account、存款线程类Save和取款线程类Fetch来实现线程间的数据共享。其中,银行账户类 Accou

26、nt 中的私有变量value用于记录账户上现有的金额,put()方法实现存款操作,get()方法实现取款操作;存款线程Save与取款线程Fetch的操作类似,在操作前,必须先确定账户上的现有金额,然后才进行存取款操作。这里可以使用sleep()方法表示存取款操作的间隔。程序的运行结果如图8-10 所示。8.4 线程的同步机制2)关键字synchronized关键字synchronized用于声明在任意时刻只能有一个线程可以执行一段代码或一个方法。它有两种方法,即锁定一段代码或锁定一个方法。(1)锁定一段代码。利用关键字synchronized可以锁定一段代码,这称为创建一个代码临界区,使得线程

27、必须等候特定资源的所有权才能运行。(2)锁定一个方法。使用关键字synchronized声明的方法称为互斥方法,表明在任意给定时刻都只能有一个线程可以执行该方法。8.4 线程的同步机制程序的运行结果如图8-11所示。8.4 线程的同步机制8.4.2 传送数据的线程同步运行1)线程间传送数据如果设计4个人打牌的游戏程序,用1个发送线程来实现发牌过程,用4个接收线程实现接收过程,发送线程与接收线程之间就存在以下同步问题。设有1个发送线程Sender,一次产生152的数字,就如同一次发出52张牌。同时有4个Recevier依次接收Sender发出的52张牌。每轮发牌中,发送线程发送4张牌,每个接收线

28、程只能接收1张牌,保证4个接收线程最终接收的牌数相等。问题的关键是,如何保证1个发送线程与4个接收线程的同步运行。为了使问题简单化,先来考虑在没有进行同步控制时,1个发送线程与1个接收线程的运行情况,再考虑有同步控制时1个发送线程与1个接收线程的运行情况。8.4 线程的同步机制程序的运行结果如图8-12 所示。8.4 线程的同步机制2)关键字synchronized与互斥锁标志 可以通过下面的方法将Buffer类中的put()方法和get()方法声明为互斥方法。synchronized void put(int i)synchornized int get()8.4 线程的同步机制那么,在程序

29、中怎样能够获得value的状态,即一个线程当前能否访问一个互斥成员呢?通常的做法是为该互斥成员设置一个能否访问的信号量,称为互斥锁标志。例如,当一个线程对共享数据进行写操作时,设置信号量为不可写,即获得互斥锁标志,则其他需要写入的线程就必须等待;当线程写完后,放弃互斥锁,信号量改为可写,则另外一个等待的写线程就可以得到写权限。这样在同一个时刻只有一个线程可以更改共享数据,从而保证数据的完整性和一致性。8.4 线程的同步机制3)线程之间的通信8.4 线程的同步机制为了更好地保证数据的完整性和一致性,Java中的Object类提供了三种方法实现线程的同步控制机制。(1)wait()方法。(2)no

30、tify()方法。(3)notifyAll()方法。程序的运行结果如图8-13所示。8.4 线程的同步机制8.4.3 死锁问题一旦有多个线程,且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其他进程或线程才可以进行的操作,那么就称它们被死锁了。最常见的死锁形式是当线程1持有对象A上的锁,而且正在等待对象B上的锁;而线程2持有对象B上的锁,却正在等待对象A上的锁,这两个线程永远都不能获得第2个锁或释放第1个锁,所以它们会永远等待下去。在这种情况下,处于等待状态的多个线程都占用了系统资源,但又无法运行,因此不会释放自己的资源。系统的资源是有限的,这将迫使程序停止运行。8.4 线程的同步机制例如,在【例8-13】的程序中虽然使用了互斥的put()方法和get()方法,但如果只有wait()方法而没有notify()方法,就会构成死锁,导致多个线程等待而程序无法继续运行和结束。Java本身不能发现死锁,也不能避免死锁,所以必须由用户自己考虑这些问题。在设计程序中,避免死锁的有效方法如下。(1)线程因为某个条件未满足而受阻,不能让其继续占有资源。(2)如果有多个对象需要互斥访问,应确定线程获得锁的顺序,并保证整个程序以相反的顺序释放锁。8.4 线程的同步机制谢谢观看!

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 大学资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com