Java语言程序设计基础教程课件(第7章)

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第7章多线程线程简介Thread类的子类创建线程实现Runnable接口基本的线程控制线程的调度多线程的互斥与同步Daemon线程7.1线程简介到目前为止所介绍过的各种范例都是单线程程序,也就是启动的Java程序在“同一时间”内只会做一件事。文本模式下最常进行的就是单线程程序。有时需要程序“同时”可以作很多事,即所谓多线程(Multi-thread)程序,在窗口程序、网络程序中常使用多线程功能,了解多线程概念与注意事项是非常重要的。7.1.1进程与线程程序是一段静态的代码,它是应用软件执行的蓝本。进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程。线程是比进程更小的执行单位,一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、存在和消亡的过程,也是一个动态的概念。Java的多线程就是在操作系统每次分时给Java程序一个时间片的CPU时间内,在若干个独立的可控制的线程之间切换。7.1.2线程的状态Java使用Thread类及其子类的对象来表示线程,线程在它的一个完整的生命周期中通常要经历如下的4种状态:1.创建状态(newThread)2.可运行状态(Runnable)3.不可运行状态(NotRunnable)4.死亡状态(Dead)7.2Thread类的子类创建线程用Thread类或子类创建线程对象.编写Thread类的子类时,需要重写父类的run方法,其目的是规定线程的具体操作,否则线程就什么也不做,因为父类的run方法中没有任何操作语句。当JVM将CPU使用权切换给线程时,如果线程是Thread的子类创建的,该类中的run方法就立刻执行。7.3实现Runnable接口创建线程的另一个途径就是用Thread类直接创建线程对象。使用Thread类创建线程对象时,常用的构造方法是:Thread(Runnabletarget);该构造方法中的参数是一个Runnable类型的接口,因此,在创建线程对象时,必须向构造方法的参数传递一个实现Runnable接口类的实例,该实例对象称为所创线程的目标对象。【例7-3】通过接口构造线程体importjava.awt.Graphics;importjava.util.Date;publicclassep7_3extendsjava.applet.AppletimplementsRunnable{//实现接口ThreadclockThread;publicvoidstart(){if(clockThread==null){clockThread=newThread(this,Clock);clockThread.start();//启动线程}}publicvoidrun(){//run()方法中是线程执行的内容while(clockThread!=null){repaint();//刷新显示画面try{clockThread.sleep(1000);//睡眠1秒,即每隔1秒执行一次}catch(InterruptedExceptione){}}}publicvoidpaint(Graphicsg){Datenow=newDate();//获得当前的时间对象g.drawString(now.getHours()+:+now.getMinutes()+:+now.getSeconds(),5,10);//显示当前时间}publicvoidstop(){clockThread.stop();clockThread=null;}}本程序是Applet,要运行Applet程序,必须定义一个html文件,ep7_3.html文件内容如下:htmlheadtitle/title/headbodyappletcodebase=.code=ep7_3.classname=width=200height=100/applet/body/html上面这个例子是通过每隔1秒种就执行线程的刷新画面功能,显示当前的时间;看起来的效果就是一个时钟,每隔1秒就变化一次。由于采用的是实现接口Runnable的方式,所以该类Clock还继承了Applet,Clock就可以Applet的方式运行。构造线程体的两种方法的比较:1.使用Runnable接口1)可以将CPU,代码和数据分开,形成清晰的模型;2)还可以从其他类继承;3)保持程序风格的一致性。2.直接继承Thread类1)不能再从其他类继承;2)编写简单,可以直接操纵线程,无需使用Thread.currentThread()。7.4基本的线程控制可以通过线程的方法进行基本的线程控制,下面我们熟悉一下常用的方法。1.start()方法线程调用该方法将启动线程,从新建状态进入就绪队列排队。一旦CPU资源轮转到它时,就脱离主线程开始自己的生命周期。2.run()方法系统的Thread类中,run()方法没有具体内容,用户需要在程序中重写run()方法来覆盖原来的run()方法,run()方法中定义线程对象被调度之后所执行的操作,是系统自动调用而用户不能引用的方法。当run()方法执行完毕,线程就变成死亡状态,在线程没有结束run()方法之前,不要让线程再调用start()方法,否则将发生IllegalThreadStateException异常,这一点在写程序的时候需要注意。3.sleep(intmillsecond)方法sleep方法可以暂停一个线程的执行,在适当的时候再恢复其执行。就是让当前线程睡眠(停止执行)若干毫秒,线程由运行中状态进入不可运行状态,停止执行时间到后线程进入可运行状态。4.isAlive()方法测试线程状态。可以通过Thread中的isAlive()方法来获取线程是否处于活动状态;线程由start()方法启动后,直到其被终止之间的任何时刻,都处于Alive状态。线程处于新建状态时,调用isAlive()方法返回false,线程进入死亡状态后,调用isAlive()方法返回false。5.currentThread()方法currentThread()方法是Thread类的类方法,可以直接通过类名调用,该方法返回当前正在使用CPU资源的线程。6.Interrupt()方法interrupt()方法常用来“吵醒”休眠的线程。但线程调用sleep方法处于休眠状态时,一个占有CPU资源的线程可以让休眠的线程调用interrupt方法唤醒自己。导致休眠的线程发生InterruptedException异常,结束休眠,重新排队等待CPU资源。7.stop()方法通过调用线程的实例方法stop()来终止线程。线程终止后,其生命周期结束了,即进入死亡态,终止后的线程不能再被调度执行。8.join()方法一个线程在占有CPU资源期间,可以让其他线程调用join()方法和本线程联合。当前线程等待调用该方法的线程结束后,再重新排队等待CPU资源,以便恢复执行。如果当前线程准备联合的线程已经结束,也就是start方法体已经执行完,那么不会产生任何效果。TimerThreadtt=newTimerThread(100);tt.start();…publicvoidtimeout(){tt.join();//当前线程等待线程tt执行完后再继续往下执行…}【例7-5】线程联合的例子。publicclassep7_5{publicstaticvoidmain(Stringargs[]){ThreadJoina=newThreadJoin();a.customer.start();a.tvMaker.start();}}classThreadJoinimplementsRunnable{TVtv;Threadcustomer,tvMaker;ThreadJoin(){customer=newThread(this);tvMaker=newThread(this);customer.setName(顾客);tvMaker.setName(电视制造厂);}publicvoidrun(){if(Thread.currentThread()==customer){System.out.println(customer.getName()+等+tvMaker.getName()+生产电视);try{tvMaker.join();//线程customer开始等待tvMaker结束}catch(InterruptedExceptione){}System.out.println(customer.getName()+买了一台电视:+tv.name+价钱:+tv.price);}elseif(Thread.currentThread()==tvMaker){System.out.println(tvMaker.getName()+开始生产电视,请等...);try{tvMaker.sleep(2000);}catch(InterruptedExceptione){}tv=newTV(红星牌,3288);System.out.println(tvMaker.getName()+生产完毕);}}}classTV{floatprice;Stringname;TV(Stringname,floatprice){this.name=name;this.price=price;}}7.5线程的调度Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪些线程来执行。线程的优先级用数字来表示,范围从1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORITY。一个线程的缺省优先级是5,即Thread.NORM_PRIORITY。下述方法可以对优先级进行操作:intgetPriority();得到线程的优先级。voidsetPriority(intnewPriority);当线程被创建后,可通过此方法改变线程的优先级。线程调度器按线程的优先级高低选择高优先级线程(进入运行中状态)执行,同时线程调度是抢先式调度,即如果在当前线程执行过程中,一个更高优先级的线程进入可运行状态,则这个线程立即被调度执行。抢先式调度又分为:时间片方式和独占方式。在时间片方式下,当前活动线程执行完当前时间片后,如果有其他处于就绪状态的相同优先级的线程,系统会将执行权交给其他就绪态的同优先级线程;当前活动线程转入等待执行队列,等待下一个时间片的调度。在独占方式下,当前活动线程一旦获得执行权,将一直执行下去,直到执行完毕或由于某种原因主动放弃CPU,或者是有一高优先级的线程处于就绪状态。下面几种情况下,当前线程会放弃CPU:1)线程调用了yield()或sleep()方法主动放弃;2)由于当前线程进行I/O访问,外存读写,等待用户输入等操作,导致线程阻塞;或者是为等候一个条件变量,以及线程调用wait()方法;3)抢先式系统下,由高优先级的线程参与调度;时间片方式下,当前时间片用完,由同优先级的线程参与调度。7.6多线程的互斥与同步经常有一些同时运行的线程需要共享数据,此时就需考虑其他线程的状态和行为,否则就不能保证程序的运行结果的正确性。7.6.1临界资源问题下面是一个堆栈的类定义:classstack{intidx=0;//堆栈指针的初始值为0char[]data=newchar[6];//堆栈有6个字符的空间publicvoidpush(charc){//压栈操作data[i

1 / 50
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功