您好,欢迎访问三七文档
当前位置:首页 > 临时分类 > Java语言程序设计基础教程课件(第7章)
第7章多线程线程简介Thread类的子类创建线程实现Runnable接口基本的线程控制线程的调度多线程的互斥与同步Daemon线程程序是为完成特定任务、用某种语言编写的一组指令的集合。指一段静态的代码。进程是程序的一次执行过程,是系统进行调度和资源分配的一个独立单位。执行就绪等待PCBCPU挂起7.1线程简介7.1.1进程与线程单线程多线程进程传统进程多线程进程7.1线程简介Java的多线程就是在操作系统每次分时给Java程序一个时间片的CPU时间内,在若干个独立的可控制的线程之间切换。7.1.2线程的状态Java使用Thread类及其子类的对象来表示线程,线程在它的一个完整的生命周期中通常要经历如下的4种状态:1.创建状态(newThread)2.可运行状态(Runnable)3.不可运行状态(NotRunnable)4.死亡状态(Dead)新建状态newThread(..)就绪状态start()等待状态执行状态I/Osleep()CPU调度run()结束stop()yield()消亡I/O完成sleep时间到7.1.2线程的状态例7-17.2Thread类的子类创建线程用Thread类或子类创建线程对象。Thread类的重要方法:run()定义线程的具体操作系统调度此线程时自动执行初始时无具体操作内容如何编程呢?例7-27.2Thread类的子类创建线程MyThreadmt=newMyThread();mt.start();classMyThreadextendsThread{publicvoidrun(){线程体…}}执行run()方法7.3实现Runnable接口Runnable接口自定义类实现Runnable接口使用Thread类的另一构造函数:Thread(Runnable,String)用实现了Runnable接口的类的对象中所定义的run()方法,来覆盖新创建的线程对象的run()方法使用start()启动线程7.3实现Runnable接口classMyRunimplementsRunnable{publicvoidrun(){线程体…}}MyRunmr=newMyRun();Threadt1=newThread(mr);t1.start();//Thread实例用于线程控制适合于:定义run()方法的类必须是其他类或其他类的子类。例7-3构造线程体的两种方法的比较: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-47.stop()方法通过调用线程的实例方法stop()来终止线程。线程终止后,其生命周期结束了,即进入死亡态,终止后的线程不能再被调度执行。8.join()方法一个线程在占有CPU资源期间,可以让其他线程调用join()方法和本线程联合。当前线程等待调用该方法的线程结束后,再重新排队等待CPU资源,以便恢复执行。TimerThreadtt=newTimerThread(100);tt.start();…publicvoidtimeout(){tt.join();//当前线程等待线程tt执行完后再继续往下执行…}例7-57.5线程的调度Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪些线程来执行。线程的优先级用数字来表示,范围从1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORITY。一个线程的缺省优先级是5,即Thread.NORM_PRIORITY。下述方法可以对优先级进行操作:intgetPriority();得到线程的优先级。voidsetPriority(intnewPriority);当线程被创建后,可通过此方法改变线程的优先级。下面几种情况下,当前线程会放弃CPU:1)线程调用了yield()或sleep()方法主动放弃;2)由于当前线程进行I/O访问,外存读写,等待用户输入等操作,导致线程阻塞;或者是为等候一个条件变量,以及线程调用wait()方法;3)抢先式系统下,由高优先级的线程参与调度;时间片方式下,当前时间片用完,由同优先级的线程参与调度。例7-67.6多线程的互斥与同步经常有一些同时运行的线程需要共享数据,此时就需考虑其他线程的状态和行为,否则就不能保证程序的运行结果的正确性。7.6.1临界资源问题下面是一个堆栈的类定义:classstack{intidx=0;//堆栈指针的初始值为0char[]data=newchar[6];//堆栈有6个字符的空间publicvoidpush(charc){//压栈操作data[idx]=c;//数据入栈idx++;//指针向上移动一位}publiccharpop(){//出栈操作idx--;//指针向下移动一位returndata[idx];//数据出栈}}两个线程A和B在同时使用Stack的同一个实例对象,A正在往堆栈里push一个数据,B则要从堆栈中pop一个数据。如果由于线程A和B在对Stack对象的操作上的不完整性,会导致操作的失败,具体过程如下所示:1)操作之前data=|p|q|||||idx=22)A执行push中的第一个语句,将r推入堆栈;data=|p|q|r||||idx=23)A还未执行idx++语句,A的执行被B中断,B执行pop方法,返回q;data=|p|q|r||||idx=14〕A继续执行push的第二个语句:data=|p|q|r||,||idx=2最后的结果相当于r没有入栈。产生这种问题的原因在于对共享数据访问的操作的不完整性。7.6.2互斥锁为解决操作的不完整性问题,在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。publicvoidpush(charc){synchronized(this){//this表示Stack的当前对象data[idx]=c;idx++;}}publiccharpop(){synchronized(this){//this表示Stack的当前对象idx--;returndata[idx];}}synchronized除了象上面讲的放在对象前面限制一段代码的执行外,还可以放在方法声明中,表示整个方法为同步方法。publicsynchronizedvoidpush(charc){…}如果synchronized用在类声明中,则表明该类中的所有方法都是synchronized的。7.6.3多线程的同步本小节将讨论如何控制互相交互的线程之间的运行进度,即多线程之间的同步问题,下面我们将通过多线程同步的模型:生产者-消费者问题来说明怎样实现多线程的同步。我们把系统中使用某类资源的线程称为消费者,产生或释放同类资源的线程称为生产者。例7-7比较一下wait()、notify()和notifyAll()方法:1)wait,nofity,notifyAll:必须在已经持有锁的情况下执行,所以它们只能出现在synchronized作用的范围内,也就是出现在用synchronized修饰的方法或类中。2)wait的作用:释放已持有的锁,进入等待队列。3)notify的作用:唤醒wait队列中的第一个线程并把它移入锁申请队列。4)notifyAll的作用:唤醒wait队列中的所有的线程并把它们移入锁申请队列。7.7Daemon线程一个Daemon线程是在后台执行服务线程,例如网络服务器侦听连接端口的服务,隐藏系统线程,垃圾收集线程或其他JVM建立的线程。当程序中所有的非Daemon的线程都结束了,即使Daemon线程的run()方法中还有需要执行的语句,也立刻结束执行。线程默认是非Daemon线程。一个线程调用voidsetDaemon(booleanon);方法可以将自己设置成一个Daemon线程。这里,参数on取值为true意味着是Daemon线程;取值为false意味着是非Daemon线程,也称用户(user)线程。一个线程必须在运行之前设置自己是否是Daemon线程。例7-8
本文标题:Java语言程序设计基础教程课件(第7章)
链接地址:https://www.777doc.com/doc-3685693 .html