第10章Java多线程2020/1/10宋波,李晋,李妙妍,张悦本章要点1.概述2.创建线程3.线程的优先级4.线程的基本控制5.线程间的同步6.线程间的通信2020/1/10宋波,李晋,李妙妍,张悦10.1概述在程序开始投入运行时,系统从程序入口开始按语句的顺序(其中包括顺序、分支和循环)完成相应指令直至结尾,从出口退出,同时整个程序结束;这样的语句结构称之为进程,或者说进程就是程序在处理机中的一次运行;2020/1/10宋波,李晋,李妙妍,张悦1什么是线程?线程是比进程单位更小一级的执行单位,在形式上同进程十分相似——都是用一个顺序执行的语句序列来完成特定的功能;一个进程在其执行过程中,可以产生多个线程,形成多条执行线索。2020/1/10宋波,李晋,李妙妍,张悦每个线程也有它自身的产生、存在和消亡的过程,也是一个动态的概念。一个线程有它自己的入口和出口,以及一个顺序执行的序列。线程不能独立存在,必须存在于进程中,各线程间共享进程空间的数据。2020/1/10宋波,李晋,李妙妍,张悦线程创建、销毁和切换的负荷远小于进程,又称为轻量级进程(lightweightprocess)。进程是由代码、数据、内核状态和一组寄存器组成;线程是由表示程序运行状态的寄存器以及堆栈组成。2020/1/10宋波,李晋,李妙妍,张悦3.Java程序中的线程多线程是指同时存在几个执行体,按几条不同的执行线索共同工作的情况。Java类库中的类java.lang.Thread允许创建并控制所创建的线程;多线程实现单个进程中的并发计算。2020/1/10宋波,李晋,李妙妍,张悦3.线程的生命周期新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。就绪:处于新建状态的线程被启动后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件。运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态,run()方法定义了线程的操作和功能。2020/1/10宋波,李晋,李妙妍,张悦3.线程的生命周期阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态。死亡:线程完成了它的全部工作或线程被提前强制性地中止,即stop()或destroy()。2020/1/10宋波,李晋,李妙妍,张悦新建状态就绪状态start()运行状态调度阻塞状态导致阻塞的事件阻塞解除终止状态run()2020/1/10宋波,李晋,李妙妍,张悦10.2创建线程10.2.1继承Thread类10.2.2实现Runnable接口2020/1/10宋波,李晋,李妙妍,张悦1.Thread类Thread类综合了Java程序中一个线程需要拥有的属性和方法。当生成一个Thread类的对象后,一个新的线程诞生了。每个线程都是通过目标对象的方法run()来完成其操作的。方法run()称为线程体(线程方法)。2020/1/10宋波,李晋,李妙妍,张悦1.Thread类在java.lang包中,Thread类声明如下:publicclassThreadextendsObjectimplementsRunnable{…privateRunnabletarget;…publicThread(){…}publicThread(Runnabletarget){…}…publicvoidrun(){……}}2020/1/10宋波,李晋,李妙妍,张悦1.Thread类通过继承Thread类创建线程的步骤如下:从Thead类派生子类,并重写其中的run()方法定义线程体。创建该子类的对象以创建线程。调用该对象的start()方法启动线程。线程启动后自动执行run()方法,线程执行完毕后进入终止状态。2020/1/10宋波,李晋,李妙妍,张悦1.Thread类的构造方法publicThread()publicThread(ThreadGroupgroup,Runnabletarget,Stringname)group-指明该线程所属的线程组。target-提供线程体的对象。name-线程名称。2020/1/10宋波,李晋,李妙妍,张悦创建线程方式1:继承Thread类1.将一个类定义为Thread的子类,那么这个类就可以用来表示线程;2.应用这种形式的构造方法创建线程对象时不用给出任何参数。2020/1/10宋波,李晋,李妙妍,张悦3.这个类中有一个至关重要的方法——publicvoidrun(),这个方法称为线程体,它是整个线程的核心,线程所要完成任务的代码都定义在线程体中,实际上不同功能的线程之间的区别就在于它们线程体的不同。2020/1/10宋波,李晋,李妙妍,张悦例10-1classSampleextendsThread{inti;publicvoidrun(){System.out.println(ThreadBegin:+this);while(true){System.out.println(HelloWorld+i+++次);if(i==3)break;}System.out.println(ThreadEnd:+this);}}2020/1/10宋波,李晋,李妙妍,张悦publicclassThreadSample{publicstaticvoidmain(String[]args){System.out.println(SystemStart:);Samples1=newSample();Samples2=newSample();s1.start();s2.start();System.out.println(SystemEnd:);}}2020/1/10宋波,李晋,李妙妍,张悦运行结果SystemStart:SystemEnd:ThreadBegin:Thread[Thread-0,5,main]ThreadBegin:Thread[Thread-1,5,main]HelloWorld0次HelloWorld1次HelloWorld2次HelloWorld0次HelloWorld1次HelloWorld2次ThreadEnd:Thread[Thread-0,5,main]ThreadEnd:Thread[Thread-1,5,main]注意程序员无法准确地知道线程在何时开始执行,因为这是由JVM来控制的,而且线程之间在执行时是相互独立的,即线程独立于启动它的程序。如果多次运行程序,就会发现每次的执行结果不一样,原因在于程序不能控制在何时执行哪一个线程。线程的执行必须调用start()方法。创建线程二——实现Runnable接口1.Runnable是Java中用以实现线程的接口,从根本上讲,任何实现线程功能的类都必须实现该接口;2.Runnable接口中只定义了一个方法就是run()方法,也就是线程体;2020/1/10宋波,李晋,李妙妍,张悦3.Thread第二种构造方法中包含有一个Runnable实例的参数;4.即,必须定义一个实现Runnable接口的类并产生一个该类的实例,对该实例的引用就是适合于这个构造方法的参数。2020/1/10宋波,李晋,李妙妍,张悦2.Runnable接口通过实现Runnable接口创建线程的步骤如下:定义一个类实现Runable接口,即在该类中提供run()方法的实现。把Runnable的一个实例作为参数传递给Thread类的一个构造方法,该实例对象提供线程体的run()方法实现。2020/1/10宋波,李晋,李妙妍,张悦classSampleimplementsRunnable{inti;publicvoidrun(){System.out.println(ThreadBegin:+this);while(true){System.out.println(HelloWorld+i+++次);if(i==3)break;}System.out.println(ThreadEnd:+this);}}2020/1/10宋波,李晋,李妙妍,张悦publicclassRunnableSample{publicstaticvoidmain(String[]args){System.out.println(SystemStart:);Threads1=newThread(newSample());Threads2=newThread(newSample());s1.start();s2.start();System.out.println(SystemEnd:);}}2020/1/10宋波,李晋,李妙妍,张悦运行结果SystemStart:SystemEnd:ThreadBegin:Sample@1fb8ee3HelloWorld0次HelloWorld1次HelloWorld2次ThreadBegin:Sample@61de33ThreadEnd:Sample@1fb8ee3HelloWorld0次HelloWorld1次HelloWorld2次ThreadEnd:Sample@61de33总结可以构造一个线程如下:Runner1r=newRunner1();Threadt=newThread(r);2020/1/10宋波,李晋,李妙妍,张悦10.3线程的优先级在单CPU情形下执行多线程时,Java采用的是优先级调度(priorityscheduling)的策略,这样就可以根据处于可运行状态的相对优先级来实现调用。所有进行可运行状态的线程首先要进入线程就绪队列中等待CPU资源,然后按照“先进先出”的原则,优先级高的排在前面。2020/1/10宋波,李晋,李妙妍,张悦10.3线程的优先级三个常量MAX_PRIORITY10;MIN_PRIORITY1;NORM_PRIORITY5;getPriority()返回线程优先值setPriority(intnewPriority)改变线程的优先级线程创建时继承父线程的优先级2020/1/10宋波,李晋,李妙妍,张悦Windows操作系统是按照时间片执行,低优先级的线程可能会抢占同级或高优先级的线程。例如,当一个优先级为5且处于可运行状态的线程在等待CPU时,系统可能正在执行一个优先级为3的线程classThreadPriorityextendsThread{publicThreadPriority(Strings){setName(s);}publicvoidrun(){//重写run方法,输出线程名和其优先级System.out.println(Thread:+getName()+:+getPriority());}2020/1/10宋波,李晋,李妙妍,张悦publicstaticvoidmain(Stringargs[]){ThreadPrioritymt1=newThreadPriority(thread1);//创建线程ThreadPrioritymt2=newThreadPriority(thread2);//创建线程ThreadPrioritymt3=newThreadPriority(thread3);//创建线程ThreadPrioritymt4=newThreadPriority(thread4);//创建线程mt1.setPriority(1);mt2.setPriority(2);mt3.setPriority(3);mt4.setPriority(4);mt1.start();mt2.start();mt3.start();mt4.start();}}2020/1/10宋波,李晋,李妙妍,张悦[例10-3]运行结果Thread:thread3:3Thread:thread4:4Thread:thread1:1Thread:thread2:210.4线程的基本控制1.相同优先级的线程的让步(yield)2.线程的休眠(sleep)3.