您好,欢迎访问三七文档
当前位置:首页 > 临时分类 > Java语言程序设计基础教程课件(第7章)
Java程序设计第七章多线程第7章多线程线程简介Thread类的子类创建线程实现Runnable接口基本的线程控制线程的调度多线程的互斥与同步Daemon线程重点内容:如何定义一个线程线程的状态转换关系图线程间如何实现共享数据线程间如何处理临界资源线程间如何实现相互通信理解生产者--消费者模型7.1线程简介理解线程理解多线程7.1线程简介程序:是一段静态的代码,它是应用软件执行的蓝本进程:是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程线程:线程是比进程更小的执行单位,一个进程在执行过程中,可以产生多个线程,形成多条执行线索。一条执行线索就是一个线程。每个线程都有各自独立的运行栈和程序计数器7.1.1进程与线程多线程实现机制:CPU时间片在多个独立的线程间切换,从宏观上看,在一个程序运行期间,有多个顺序流同时执行多线程代码和数据共享代码可以或不可以由多个线程共享。如果两个线程执行同一个类的实例代码,则它们可以共享相同的代码。数据可以或不可以由多个线程共享。如果两个线程共享对一个公共对象的存取,则它们可以共享相同的数据。7.1.2线程的状态线程在它的一个完整的生命周期中通常要经历如下的几种状态:1.创建(newThread)2.可运行(Runnable)3.运行(Running)4.阻塞(NotRunnable)5.死亡(Dead)Start()Run()执行完123定义线程的方法1.实现Runnable接口publicclassmythreadimplementsRunnable2.定义Thread类的子类publicclassmythreadextendsThreadpublicvoidrun():是线程运行的主体,当JVM将CPU使用权切换给线程,启动线程时,run方法就立刻执行。7.2Thread类的子类创建线程编写Thread类的子类时,需要重写父类的run方法,其目的是规定线程的具体操作,否则线程就什么也不做,因为父类的run方法中没有任何操作语句。P170例7-27.3实现Runnable接口用Thread类直接创建线程对象。使用Thread类创建线程对象时,常用的构造方法是:Thread(Runnabletarget);在创建线程对象时,向构造方法的参数传递一个实现Runnable接口类的实例,该实例对象称为所创线程的目标对象。P171【例7-3】通过接口构造线程体publicclassep7_2_2implementsRunnable{Threadteacher=newThread(this);Threadstudent=newThread(this);ComputerSumsum=newComputerSum();publicep7_2_2(){teacher.start();student.start();}publicstaticvoidmain(String[]args){newep7_2_2();}publicvoidrun(){if(Thread.currentThread()==teacher){Thread.currentThread().setName(teacher);}else{Thread.currentThread().setName(student);}for(inti=1;i=5;i++){intm=sum.getSum();sum.setSum(m+1);System.out.println(我是+Thread.currentThread().getName()+,现在的和:+sum.getSum());try{Thread.sleep(200);}catch(InterruptedExceptione){}}}构造线程体的两种方法的比较:1.使用Runnable接口1)线程间容易实现共享数据和代码2)可以继承于其他类;2.直接继承Thread类1)不能再继承其他类(不利于代码升级);2)编写简单,可以直接操纵线程两种方式下如何实现多线程间的数据共享?7.4操作线程的常用方法可以通过线程的方法进行基本的线程控制,常用方法如下:1.start()方法线程调用该方法将启动线程,从新建状态进入就绪队列排队。一旦CPU资源轮转到它时,就脱离主线程开始自己的生命周期。2.run()方法run()方法中定义线程对象被调度之后所执行的操作,是系统自动调用而用户不能引用的方法当run()方法执行完毕,线程就变成死亡状态在线程没有结束run()方法之前,不要让线程再调用start()方法,否则将发生IllegalThreadStateException异常,这一点在写程序的时候需要注意。3.sleep(intmillsecond)方法sleep方法可以暂停一个线程的执行,在适当的时候再恢复其执行。就是让当前线程睡眠(停止执行)若干毫秒,线程由运行中状态进入不可运行状态,停止执行时间到后线程进入可运行状态。Sleep()阻塞状态可运行状态4.isAlive()方法获取线程是否处于活动状态;线程由start()方法启动后,直到其被终止之间的任何时刻,都处于Alive状态。线程处于新建状态时,调用isAlive()方法返回false,线程进入死亡状态后,调用isAlive()方法返回false。5.currentThread()方法currentThread()方法是Thread类的类方法,可以直接通过类名调用,该方法返回当前正在使用CPU资源的线程。6.Interrupt()方法interrupt()方法常用来“吵醒”休眠的线程。但线程调用sleep方法处于休眠状态时,一个占有CPU资源的线程可以调用interrupt方法唤醒休眠中的线程自己。会导致休眠的线程发生InterruptedException异常,结束休眠,重新排队等待CPU资源,执行sleep()后的代码。Sleep()阻塞状态可运行状态ABA.ingerrupt()7.stop()方法_已过时通过调用线程的实例方法stop()来终止线程。线程终止后,其生命周期结束了,即进入死亡态,终止后的线程不能再被调度执行。8.join()方法一个线程在占有CPU资源期间,可以让其他线程调用join()方法和本线程联合。当前线程等待调用该方法的线程结束后,再重新排队等待CPU资源,以便恢复执行。如果当前线程准备联合的线程已经结束,也就是start方法体已经执行完,那么不会产生任何效果。P175【例7-5】线程联合的例子7.5线程的调度Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪些线程来执行。线程的优先级用数字来表示,范围从1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORITY。一个线程的缺省优先级是5,即Thread.NORM_PRIORITY。下述方法可以对优先级进行操作:intgetPriority();得到线程的优先级。VoidsetPriority(intnewPriority);当线程被创建后,可通过此方法改变线程的优先级。7.6多线程的互斥与同步经常有一些同时运行的线程需要共享数据,此时就需考虑其他线程的状态和行为,否则就不能保证程序的运行结果的正确性。7.6.1临界资源问题下面是一个堆栈的类定义:classstack{intindex=0;//堆栈指针char[]data=newchar[6];//堆栈空间publicvoidpush(charc){//压栈操作data[index]=c;//数据入栈index++;//指针向上移动一位}publiccharpop(){//出栈操作index--;//指针向下移动一位returndata[index];//数据出栈}}pqr①②7.6.2互斥锁或对象锁为解决操作的不完整性问题,引入了对象互斥锁,来保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,互斥锁可以保证在任一时刻,只能有一个线程访问该对象。Synchronized(objecto){需要一次执行完的语句块}publicvoidpush(charc){synchronized(this){data[idx]=c;idx++;}}publicsynchronizedcharpop(){idx--;returndata[idx];}同步代码同步方法注意:1、所有存取共享数据的方法必须在同一把锁上同步2、由同步锁保护的数据应为private的死锁死锁:有多个线程竞争多个资源,就可能会产生死锁两个线程相互等待来自对方的锁它不能被监测到或避免它可以通过以下方法来避免决定获取锁的次序始终遵照这个次序按照相反的次序释放锁publicclassDeadlockRisk{privateResourceA=newResource();privateResourceB=newResource();publicintread(){synchronized(A){synchronized(B){returnB.value+A.value;}}}publicvoidwrite(inta,intb){synchronized(B){synchronized(A){A.value=a;B.value=b;}}}}7.6.3多线程的同步生产者-消费者问题我们把系统中使用某类资源的线程称为消费者,产生或释放同类资源的线程称为生产者。abcdeABabcdeABABwait()aAB放弃锁,进入等待对列,等待通知发送通知给等待对列中的线程notify()notifyAll()在下面的Java的应用程序中,生产者线程向文件中写数据,消费者从文件中读数据,这样,在这个程序中同时运行的两个线程共享同一个文件资源。通过这个例子我们来了解怎样使它们同步。P179例7-7注意:wait,nofity,notifyAll:必须在同步环境中调用。线程只有拥有了某个对象的锁,才能调用该对象上的等待(wait)和通知(nofity/notifyAll)方法保证一个竞争资源用一把锁,不同竞争资源用不同的锁,最大限度实现线程的并发比较一下wait()、notify()和notifyAll()方法:1)wait的作用:释放已持有的锁和占用的CPU资源,进入对象的消息等待队列2)notify的作用:唤醒消息等待队列中的第一个线程并把它移入对象锁申请队列。3)notifyAll的作用:唤醒wait队列中的所有的线程并把它们移入对象锁申请队列。7.7Daemon线程一个Daemon线程是在后台执行服务线程当程序中所有的非Daemon的线程都结束了,即使Daemon线程的run()方法中还有需要执行的语句,也立刻结束执行。线程默认是非Daemon线程。一个线程调用voidsetDaemon(booleanon);方法可以将自己设置成一个Daemon线程。这里,参数on取值为true意味着是Daemon线程;取值为false意味着是非Daemon线程,也称用户(user)线程。一个线程必须在运行之前设置自己是否是Daemon线程。【例7-8】Daemon简单的示范publicclassep7_8{publicstaticvoidmain(String[]args){Threadthread=newThread(newRunnable(){publicvoidrun(){while(true){System.out.print(T);}}});thread.setDaemon(true);//设置为Daemon线程thread.start();}}这个程序在主线程结束之后,Daemon线程也就会跟着结束,可以使用isDaemon()方法则可以判断该线程是否为Daemon线程。下面几种情况下,当前线程会放
本文标题:Java语言程序设计基础教程课件(第7章)
链接地址:https://www.777doc.com/doc-3685796 .html