Java语言实用教程第10章.ppt_第1页
Java语言实用教程第10章.ppt_第2页
Java语言实用教程第10章.ppt_第3页
Java语言实用教程第10章.ppt_第4页
Java语言实用教程第10章.ppt_第5页
已阅读5页,还剩47页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第10章多线程机制,10.1多线程的概念10.2创建线程10.3线程的优先级10.4线程的控制10.5线程的同步机制与共享资源10.6何时使用多线程及注意问题习题,程序(program)是对数据描述与操作的代码的集合,是应用程序执行的脚本。进程(process)是程序的一次执行过程,是操作系统运行程序的基本单位。程序是静态的,进程是动态的。系统运行一个程序就是一个进程从创建、运行到消亡的过程。系统可以为一个程序同时创建多个进程。每一个进程都有自己独立的一块内存空间和一组系统资源,即使同类进程之间也不会共享系统资源。,10.1多线程的概念10.1.1程序、进程和多任务,多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每一个任务对应一个进程。由于一个CPU在同一时刻只能执行一个程序中的一条指令。实际上,多任务运行的并发机制使这些任务交替运行,因间隔时间短,所以感觉就是多个程序在同时运行。,运行一个程序时,程序内部的代码都是按顺序先后执行的。如果能够将一个进程划分为更小的运行单位,则程序中一些彼此相对独立的代码段可以重叠运行,将会获得更高的执行效率。线程就是解决这个问题的。线程是比进程更小的运行单位,是程序中单个顺序的流控制。一个进程中可以包含多个线程。线程是一种特殊的多任务方式。当一个程序执行多线程时,可以运行两个或更多的由同一个程序启动的任务。这样,一个程序可以使得多个活动任务同时发生。,10.1.2线程,线程与任何一个程序一样有一个开始、一系列可执行的命令序列、一个结束。在执行的任何时刻,只有一个执行点。线程与程序不同的是线程本身不能运行,它只能包含在程序中,只能在程序中执行。一个线程在程序运行时,必须争取到为自己分配的系统资源,如执行堆栈、程序计数器,等等。,多线程是相对于单线程而言的,指的是在一个程序中可以定义多个线程并同时运行它们,每个线程可以执行不同的任务。与进程不同的是,同类多线程共享一块内存空间和一组系统资源,所以,系统创建多线程开销相对较小。因此,也称线程为轻负荷进程。多线程和多任务是两个既有联系又有区别的概念。多任务是针对操作系统而言的,代表着操作系统可以同时执行的程序个数;多线程是针对一个程序而言的,代表着一个程序内部可以同时执行的线程个数,而每个线程可以完成不同的任务。,10.1.3多线程,1.线程的生命周期与状态一个线程也有从创建、运行到消亡的过程,称为线程的生命周期。使用线程的状态表明线程处于生命周期的哪个阶段。线程有创建(New)、可运行(Runnable)、运行中(Running)、挂起(NotRunnable)、死亡(Dead)5种状态。通过线程的控制和调度可使线程在这几种状态间转化。Java语言内在支持多线程,所有的类都是在多线程思想下定义的。Java的每个程序自动拥有一个线程,称为主线程。当程序加载到内存时,启动主线程。要加载其他线程,程序就要使用Thread类(专门用来创建和控制线程的类)或Runnable接口。,10.1.4线程的生命周期与Java的多线程机制,2.Java的多线程机制java.lang中的线程类Thread封装了所有需要的线程操作控制,有很多方法用来控制一个线程的运行、休眠、挂起或停止。这就是Java的多线程机制。使用Java的多线程机制编程可将程序的任务分解为几个并行的子任务,通过线程的并发执行来加速程序运行,提高CPU的利用率。,例10.1在程序中通过继承Thread类创建一个线程子类testThread,通过Thread1主类同时运行两个线程对象t1和t2,运行结果如图10.1所示。classThread1publicstaticvoidmain(Stringargs)testThreadt1=newtestThread(thread1);testThreadt2=newtestThread(thread2);t1.start();t2.start();,10.2创建线程10.2.1通过继承Thread类创建线程,classtestThreadextendsThreadpublictestThread(Stringstr)super(str);/调用父类的构造方法为线程对象命名publicvoidrun()for(inti=0;i3;i+)System.out.println(getName()+在运行);trysleep(1000);/用休眠1000毫秒来区分哪个线程在运行System.out.println(getName()+在休眠);catch(InterruptedExceptione)System.out.println(getName()+已结束);,图10.1,(1)Application应用程序运行时总是调用main方法,因此main是创建和启动线程对象的地方。main本身也是一个线程,是程序自动拥有的一个线程,称为主线程。在main方法中创建了两个testThread线程对象:t1与t2,并在创建后马上调用start方法启动了这两个线程。(2)从输出的结果可以看出两个线程的名字是交替显示的,这是因为两个线程是同步的,于是,两个run方法也同时被执行。线程语句的顺序只是决定于线程执行的顺序,线程的执行顺序是由系统调度和控制的。,(3)由继承Thread创建的子类,必须覆盖run方法,因为Thread线程类的run方法是空的。run是线程类的关键方法,线程的所有活动都是通过它来实现的。当调用线程对象时系统就自动调用其run方法,正是通过run方法才使创建线程的目的得以实现。run方法的作用如同Application应用程序的main方法一样。由例子可以看出,创建线程对象,就是设计run方法。一旦启动线程对象,就进入run方法,便执行run中的所有语句,run方法执行完毕,这个线程也就结束了。,通过Thread类创建线程子类的格式为:class线程的类名extendsThreadpublicvoidrun()程序语句,当一个类是从其他类继承时,如继承Applet类。此时就不能再继承Thread类来创建线程,这时可以通过接口Runnable直接创建线程对象。接口Runnable是一个抽象接口,接口中只声明了一个未实现的run方法。,10.2.2通过Runnable接口创建线程,例10.2通过Runnable接口运行线程,运行结果如图10.2所示。importjava.awt.*;importjava.applet.Applet;importjava.util.*;importjava.text.DateFormat;publicclassClock2extendsAppletimplementsRunnableThreadclockThread=null;publicvoidinit()setBackground(Color.blue);setForeground(Color.yellow);publicvoidstart()if(clockThread=null)clockThread=newThread(this,Clock2);clockThread.start();,publicvoidrun()ThreadmyThread=Thread.currentThread();while(clockThread=myThread)repaint();tryThread.sleep(1000);catch(InterruptedExceptione)publicvoidpaint(Graphicsg)Datedate=newDate();DateFormatformatter=DateFormat.getTimeInstance();Stringstr=formatter.format(date);g.drawString(str,5,10);,publicvoidstop()clockThread=null;图10.2通过例10.2可以了解通过接口创建线程对象是通过Thread类直接创建线程对象的。,在Applet的start方法中用new操作符创建了一个线程对象clockThread:clockThread=newThread(this,Clock2);clockThread.start();此时,线程clockThread处于NewThread创建状态,是一个空线程,因为还没有为它分配系统资源。处于创建状态的线程除了可以调用start方法开始线程外,不能调用其他方法,否则将产生IllegalThreadStateException异常。,调用线程的start方法后,将为线程分配系统资源,并将线程加入线程队列,然后调用run方法运行线程。此时,线程转入Runnable可运行状态。由于大部分计算机只有一个CPU,不可能同时运行所有处于Runnable状态的线程,于是Java虚拟机就建立了一个线程队列,让这些线程以排队的方式轮流使用CPU。在实现接口Runnable的run方法中,又通过Thread.currentThread方法创建了一个当前运行的线程对象myThread:ThreadmyThread=Thread.currentThread();,设计Thread的run方法。因为Thread的run方法是空方法,而run方法是完成线程任务的根本途径,本例中通过接口Runnable来实现run方法的具体设计:首先创建一个线程对象myThread,然后根据判断条件如果当前线程是clockThread,就进入循环。在循环中先执行repaint方法,它将调用paint显示系统时间。然后令线程休眠1000毫秒,此时线程将停止运行。休眠结束时线程被唤醒,如果CPU可用,就会继续运行这个线程,否则将排队等待。由本例可知,接口Runnable只是提供了一个可以实现run方法的途径。,两种创建线程方法的比较:(1)由继承Thread类创建线程对象简单方便,可以直接操作线程,但不能再继承其他类;(2)在继承其他类的类中可用Runnable接口创建线程对象。可保持程序风格的一致性。,大部分计算机都是一个CPU,一个时刻只能运行一个线程。如果有多个线程处于可运行状态,需要排队等待CPU资源,此时线程自动获得一个线程的优先级,CPU资源分配是根据“先到先服务”的原则确定的。Java为了使有些线程可以提前得到服务,可给线程设置优先级。在单个CPU上运行多线程时采用了线程队列技术,Java虚拟机支持固定优先级队列,一个线程的执行顺序取决于其相对其他Runnable线程的优先级。,10.3线程的优先级,线程在创建时,继承了父类的优先级。线程创建后,可以在任何时刻调用setPriority方法改变线程的优先级。优先级为110,Thread定义了其中3个常数:(1)MAX_PRIORITY,最大优先级(值为10)。(2)MIN_PRIORITY,最小优先级(值为1)。(3)NORM_PRIORITY,默认优先级(值为5)。,例10.3线程优先级的使用情况如图10.3所示。图10.3,classThread2extendsThreadpublicstaticvoidmain(Stringargs)Thread2t=newThread24;for(inti=0;i4;i+)ti=newThread2();for(inti=0;i4;i+)ti.start();t1.setPriority(MIN_PRIORITY);t3.setPriority(MAX_PRIORITY);publicvoidrun()for(inti=0;i1000000;i+);System.out.println(getName()+线程的优先级是+getPriority()+已计算完毕!);,通过线程的方法可以控制线程的状态。1.挂起一个线程如果要暂停一个线程可使用suspend()方法:t1.suspend(),t1为线程对象实例名称。在想运行线程时,用resume()方法重新激活线程:t1.resume()。2.停止一个线程用stop()方法可以停止线程的执行:t1.stop()。注意:这并没有消灭这个线程,只是停止了线程的执行。但这个线程不能用t1.start()重新启动。,10.4线程的控制,3.线程休眠如果让线程停止运行几毫秒可使用sleep(long)方法,上面已经提到。4.连接线程join()方法可以中止当前线程,等待调用该方法的线程完成后再继续本线程。5.暂停线程调用yield()方法可以暂停当前运行线程运行,但处于可运行状态。其他线程可照样运行。若无其他线程运行,则继续运行该线程。,6.中断线程如果要中断一个运行中的线程可使用interrupt()方法:errupt()。要运行该线程可使用t1.start()重新启动。7.了解线程的状态如果要知道线程处于什么状态,可通过线程的isAlive()方法来了解,它将返回线程当前的状态。如果返回值为true,说明线程处于Runnable或NotRunnable状态;如果返回值为false,说明线程处于NewThread或Dead状态。该方法无法进一步区分具体状态。,前面的线程例子都是独立的,而且异步执行,也就是说每个线程都包含了运行时所需要的数据和方法,不需要外部资源,也不用关心其他线程的状态和行为。但有时一些同时运行的线程需要共享数据。因此,编程时必须考虑其他线程的状态和行为,以解决资源共享问题。Java提供了同步设定功能。共享对象可将自己的成员方法定义为同步化(synchronized)方法,通过调用同步化方法来执行单一线程,其他线程则不能同时调用同一个对象的同步化方法。,10.5线程的同步机制与共享资源,例10.4生产者和消费者线程同步化问题示例。使用某种资源的线程称为消费者,产生或释放这个资源的线程称为生产者。生产者生成10个整数(09),存储到一个共享对象中,并把它们打印出来。每生成一个数就随机休眠0100ms,然后重复这个过程。一旦这10个数可以从共享对象中得到,消费者将尽可能快地消费这10个数,即把它们取出后打印出来。这个模型由4个程序组成。,1.生产者程序publicclassProducerextendsThreadprivateShareshared;privateintnumber;publicProducer(Shares,intnumber)shared=s;this.number=number;publicvoidrun()for(inti=0;i10;i+)shared.put(i);System.out.println(生产者+this.number+输出的数据为:+i);trysleep(int)(Math.random()*100);catch(InterruptedExceptione),2.消费者程序publicclassConsumerextendsThreadprivateShareshared;privateintnumber;publicConsumer(Shares,intnumber)shared=s;this.number=number;publicvoidrun()intvalue=0;for(inti=0;ii)value=value-i;/取走时,value值减少else/账户金额不够所取时i=value;value=0;/取走全部所余金额returni;inthowmatch()/查看账户上现有金额方法returnvalue;,classSaveextendsThread/存款线程privateAccount1a1;privateintamount;publicSave(Account1a1,intamount)this.a1=a1;this.amount=amount;publicvoidrun()synchronized(a1)/锁定账户对象intk=a1.howmatch();trysleep(1);/花费时间catch(InterruptedExceptione)System.out.println(e);a1.put(amount);System.out.println(现有+k+,存入+amount+,余额+a1.howmatch();publicstaticvoidmain(Stringargs),Account1a1=newAccount1();(newSave(a1,100).start();(newSave(a1,200).start();(newFetch(a1,500).start();classFetchextendsThread/取款线程privateAccount1a1;privateintamount;publicFetch(Account1a1,intamount)this.a1=a1;this.amount=amount;publicvoidrun()synchronized(a1)/锁定账户对象intk=a1.howmatch();trysleep(1);/花费时间catch(InterruptedExceptione),System.out.println(e);System.out.println(现有+k+,取走+a1.get(amount)+,余额+a1.howmatch();,在这里使用的同步方法非常简单,仅使用了synchronized关键字锁定了账户对象a1,保证同一时间内只有一个线程可以获取这个对象。使用synchronized关键字锁定对象资源后,只有在该线程获得共享资源对象后,方可运行。任何时刻都只有一个获得共享资源对象的程序在运行,其他线程在获得共享资源对象前处于等待状态。这种方式称为“同

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论