您好,欢迎访问三七文档
1.理解进程和线程的概念,学习java中线程的使用;2.掌握线程的状态和生命周期、线程的调度和控制方法;3.理解多线程的互斥和同步的实现原理,以及多线程的应用,能够熟练编写关于线程、线程间的同步与通信的小程序第9章多线程教学目的要求9.1多线程的概念进程和线程•进程的定义:一个具有独立功能的程序关于某个数据集合的一次运行活动。一个进程由一个或多个线程运行,线程是操作系统分配CPU运行时间最小单位。•线程的定义:线程是进程中可独立调度执行的子任务,一个进程可以有一个或多个线程,它们共享所属进行所拥有的资源。多线程•多线程是这样一种机制,它允许在程序中“并行”执行多个指令流,每个指令流被称作一个线程,彼此间的执行互相独立。多线程需要操作系统的支持,WIN32平台支持多线程程序,允许程序中存在多个线程。在单CPU计算机系统中,系统把CPU的时间片按照调度算法分配给各个线程,因此各线程实际上是分时执行的,而在多CPU的计算机系统中,同一个程序的不同线程可以分配到不同的CPU上去执行。多个线程的执行是并发的,也就是在逻辑上“同时”,而不是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。线程的运行•一个程序并发的运行多个线程,这些线程同时在多处理器上运行,也可以在单处理器系统中分享CPU时间。任务1任务2任务3任务1任务2任务3多线程在多个CPU上运行多线程分享单个CPU多线程的优点•多线程使程序反应更快,交互性更强,并能提供执行效率。多线程使得程序员可以编写高效的程序,因此它充许使用大多数程序中的空闲时间,大多数IO设备,包括网络端口,磁盘驱动器和键盘,都比CPU运行得慢,因此一个程序的运行时间大部分经常花费在等待设备传输信息,通过多线程可以利用这个空闲时间。线程的创建•在Java中,每个程序至少自动拥有一个线程,称为主线程,当程序加载到内存时,启动主线程,如果需要使用其它线程,则可以采用以下两种方式创建新的线程:一种是扩展java.lang.Thread类,用它覆盖Thread类的run()方法;另一种是编写一个类,使之实现java.lang.Runnable接口,然后在Thread构造函数中使用它。•第一种方式只能在类没有扩展其它任何类的情况下才能使用,因为Java不允许多重继承。因此,如果一个类要继承其它的类,最好选用第二种方法,这样会有更大的灵活性。下面分别介绍两种创建线程的方式。1.扩展Thread类•类Thread位于java.lang包中,由于java.lang包自动被导入每个Java程序中,所以可以直接使用类Thread而无需在Java程序开始处编写import语句,也这说明了Java对线程支持的彻底性。通过这个类中的方法,可以启动、终止、中断线程以及查询、设置线程的当前状态。•使用扩展Thread类的方式创建并执行一个线程,需要执行下面4个步骤:扩展java.lang.Thread的类;用希望的执行代码来实现run()方法;通过new关键字实例化该类的一个新对象(即一个线程);通过调用start()方法启动线程。例:通过继承Thread类来实现多线程publicclassTestThread{publicstaticvoidmain(String[]args){PrintCharprintA=newPrintChar('a',100);PrintCharprintB=newPrintChar('b',100);PrintNumprint100=newPrintNum(100);print100.start();printA.start();printB.start();}}classPrintCharextendsThread{privatecharcharToPrint;//Thecharactertoprint;privateinttimes;//thetimestorepeat;publicPrintChar(charc,intt){charToPrint=c;times=t;}publicvoidrun(){for(inti=1;itimes;i++){System.out.print(charToPrint);}}}classPrintNumextendsThread{privateintLastNum;publicPrintNum(intn){LastNum=n;}publicvoidrun(){for(inti=1;i=LastNum;i++)System.out.print(+i);}}2.实现Runnable接口•利用Runnable接口创建和运行线程的编程步骤为:第1步:定义一个Runnable接口的实现类,如MyThread,其内必须实现Runnable接口所声明的run()方法。定义Runnable接口的方法如下:publicclassyourThreadimplementsRunnable{publicvoidrun(){...//需要以线程方式运行的代码}}第2步:创建一个Thread类的对象,即创建一个线程,调用Thread类带Runnable引用作为形参的构造方法,把Runnable接口实现类对象传递给Thread类的对象即传递给新线程,为新线程提供程序代码和数据。如:yourThreadyourt=newyourThread();Threadtt=newThread(yourt);第3步:用线程对象调用start()方法启动线程。如:tt.start();定义一个实现了Runnable接口的类,将该类的对象作为Thread类的构造方法的参数,生成的Thread对象即为想要创建的线程,这样的线程同样通过start()方法启动。将上例改为通过接口Runnable创建线程的实例。publicclassTestRunable{publicstaticvoidmain(String[]args){ThreadprintA=newThread(newPrintCharRunable('a',100));ThreadprintB=newThread(newPrintCharRunable('b',100));Threadprint100=newThread(newPrintNumRunable(100));print100.start();printA.start();printB.start();}}classPrintCharRunableimplementsRunnable{privatecharcharToPrint;//Thecharactertoprint;privateinttimes;//thetimestorepeat;publicPrintCharRunable(charc,intt){charToPrint=c;times=t;}publicvoidrun(){for(inti=1;itimes;i++){System.out.print(charToPrint);}}}classPrintNumRunableimplementsRunnable{privateintLastNum;publicPrintNumRunable(intn){LastNum=n;}publicvoidrun(){for(inti=1;i=LastNum;i++)System.out.print(+i);}}•从以上创建线程的实例可以看出,构造线程体的两种方法各自的优缺点分析如下:1.使用Runnable接口•可以从其他类继承,当一个线程已继承了另一个类时,就只能用实现Runnable接口的方法来创建线程;•便于保持程序风格的一致性。2.扩展Thread类•不能再从其他类继承,适用于单继承线程情况;•编写简单,可以直接操作线程;由以上分析可知,两种方法各有利弊,读者应该根据实际情况灵活运用。线程的状态与控制•在这里需要明确的是:无论采用扩展Thread类还是实现Runnable接口的方法来实现应用程序的多线程能力,都需要在该类中定义用于完成实际功能的run()方法,这个run()方法称为线程体(Threadbody)。按照线程体在计算机系统内存中的状态,可以将线程从产生到灭亡分为新建、就绪、运行、挂起、死亡等5种状态。新建新建状态运行状态就绪状态挂起状态死亡状态start()wait()sleep()notify()唤醒run()结束run()结束run()结束stop()stop()stop()调度图9.3线程的生命周期•新建状态:线程在已经利用new关键字创建但是还未执行的这段时间里,处于一种特殊的新建状态中,此时,线程对象已经被分配了内存空间,私有数据已经被初始化,但是该线程尚未被调度。此时的线程可以被调度,变成可运行状态,也可以被杀死,变成死亡状态。•就绪状态:在处于创建状态的线程中调用start()方法将线程的状态转换为就绪状态。这时,线程已经得到除CPU时间之外的其它系统资源,只等JVM(Java虚拟机)的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会。•运行状态:运行状态表明线程正在运行,该线程已经拥有了对CPU的控制权。这个线程一直运行到运行完毕,除非该线程主动放弃CPU的控制权或者CPU的控制权被优先级更高的线程抢占。处在运行状态的线程在下列情况下将让出CPU的控制权:有比当前进程优先级更高的线程进入可运行状态;线程主动睡眠一段时间;线程运行完毕;线程在等待某一资源。•挂起状态:如果一个线程处于挂起状态,那么暂时这个线程无法进入就绪队列。处于挂起状态的线程通常需要由某些事件才能唤醒,至于由什么事件唤醒该线程,则取决于其挂起的原因。处于睡眠状态的线程必须被挂起一段固定的时间,当睡眠时间结束时就变成可运行状态;因等待资源或消息而被挂起的线程则需要由一个外来事件唤醒。•死亡状态:正常情况下run()返回使得线程死亡。调用stop()或destroy()亦有同样效果,但是不被推荐,因为前者会产生异常,后者是强制终止,不会释放内存。同其它Java类一样,Thread类中也有许多有用的方法,在这里只介绍部分常用的方法:1.Publicvoidsuspend():挂起线程,可能引起死锁。2.publicstaticvoidsleep(longmillis);使当前执行的线程睡眠指定的时间。参数millis是线程睡眠的毫秒数。如果这个线程已经被别的线程中断,就会产生InterruptedException异常。3.publicvoidstart();使线程由新建状态变成可运行状态。4.publicvoidrun();java运行系统调用该方法来执行线程。用户线程类必须覆盖该方法并且提供线程执行的代码。程序中它永远不能被可运行对象直接调用。5.publicfinalvoidstop();停止(杀死)当前线程。6.publicfinalbooleanisAlive();测试线程是否处于活动状态,即已启动,但还没有终止。7.Publicstaticbooleanisinterrupted():测试当前线程是否被中断8.publicfinalvoidsetPriority(intnew);改变线程的优先级。9.publicstaticThreadcurrenthread();返回当前执行线程对象的引用。10.publicfinalvoidnotify();唤醒一个等待中的线程。9.3线程的同步线程同步的概念•由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问,这套机制就是线程同步。•线程同步是指Java避免多个线程同时访问一个数据而造成数据混乱的方法。它可以避免多个线程同
本文标题:Java(多线程)
链接地址:https://www.777doc.com/doc-3376701 .html