Java语言程序设计(第三版清华)第8章new.ppt

上传人:牧羊曲112 文档编号:6510203 上传时间:2023-11-07 格式:PPT 页数:67 大小:793KB
返回 下载 相关 举报
Java语言程序设计(第三版清华)第8章new.ppt_第1页
第1页 / 共67页
Java语言程序设计(第三版清华)第8章new.ppt_第2页
第2页 / 共67页
Java语言程序设计(第三版清华)第8章new.ppt_第3页
第3页 / 共67页
Java语言程序设计(第三版清华)第8章new.ppt_第4页
第4页 / 共67页
Java语言程序设计(第三版清华)第8章new.ppt_第5页
第5页 / 共67页
点击查看更多>>
资源描述

《Java语言程序设计(第三版清华)第8章new.ppt》由会员分享,可在线阅读,更多相关《Java语言程序设计(第三版清华)第8章new.ppt(67页珍藏版)》请在三一办公上搜索。

1、第九章 线程,2,目录,Java中的线程线程的生命周期Thread的子类创建线程使用Runable接口线程的常用方法线程的优先级线程同步在同步方法中使用wait()、notify 和notifyAll()方法挂起、恢复和终止线程本章小结,3,线程的概念,进程和线程的区别(Example9_11)进程一个独立程序的每一次运行称为一个进程,例如用字处理软件编辑文稿时,同时打开mp3播放程序听音乐,这两个独立的程序在同时运行,称为两个进程设置一个进程要占用相当一部分处理器时间和内存资源大多数操作系统不允许进程访问其他进程的内存空间,进程间的通信很不方便,编程模型比较复杂,进程通信方式 共享存储器系统

2、、消息传到机制、管道通信,4,线程一个程序中多段代码同时并发执行,称为多线程通过多线程,一个进程表面上看同时可以执行一个以上的任务并发创建线程比创建进程开销要小得多,线程之间的协作和数据交换也比较容易(进程独站资源)线程间共享资源(内存、代码、数据)有利于并行处理,线程的概念(续),5,线程的状态与生命周期,新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。此时它已经有了相应的内存空间和其他资源。就绪状态:线程等待 CPU运行状态:start()方法开始执行中断(阻塞)状态:sleep(),wait(),I/O 完成 or 发布 I/O 请求sleep():使

3、当前线程进入等待状态,参数设定其等待时间wait():使当前线程进入等待状态,调用nofify(),or notifyAll(),使其重新进入线程等待队列死亡:run()方法完成 stop()方法被调用,6,Life Cycle of Thread,7,诞生状态线程刚刚被创建就绪状态线程的 start 方法已被执行线程已准备好运行运行状态处理机分配给了线程,线程正在运行阻塞状态(Blocked)在线程发出输入/输出请求且必须等待其返回遇到用synchronized标记的方法而未获得其监视器暂时不能进入执行时休眠状态(Sleeping)执行sleep方法而进入休眠死亡状态线程已完成或退出,线程的

4、几种基本状态(续),8,主线程:Java应用程序总是从主类的main方法开始执行。当JVM加载代码,发现main方法之后,启动的线程称作“主线程”,该线程负责执行main方法。在main方法的执行中再创建的线程,就称为程序中的其它线程。如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,JVM就会结束Java应用程序。如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,main方法即使执行完最后的语句,JVM也不会结束程序,JVM一直要等到程序中的所有线程都结束之后,才结束Java应用程序。,9,Java中创建线程的两种方法Thread 类

5、的子类创建线程对象,子类重写Thread类中的run()方法使用Thread类直接创建线程对象,但需要使用Runable接口,10,Thread类,Thread类在Java程序中创建多线程的方法之一是继承Thread类 封装了Java程序中一个线程对象需要拥有的属性和方法从Thread类派生一个子类,并创建这个子类的对象,就可以产生一个新的线程。这个子类应该重写Thread类的run()方法,在run方法中写入需要在新线程中执行的语句段。这个子类的对象需要调用start方法来启动,新线程将自动进入run方法。原线程将同时继续往下执行它位于java.lang包中,因而程序开头不用import任何

6、包就可直接使用例子:Example9_1.java,public class Example9_1 public static void main(String args)Lefthand left;Righthand right;left=new Lefthand();/创建线程 right=new Righthand();left.start();right.start();for(int i=1;i=3;i+)(我是主线程);,class Lefthand extends Thread public void run()for(int i=1;i=3;i+)(我是左手线程);class

7、Righthand extends Thread public void run()for(int i=1;i=3;i+)(我是右手线程);,我是主线程我是主线程我是主线程我是左手线程我是左手线程我是左手线程我是右手线程我是右手线程我是右手线程,12,Thread类(续),在新线程中完成计算某个整数的阶乘public class Ex8_1 public static void main(String args)System.out.println(main thread starts);FactorialThread thread=new FactorialThread(10);thread

8、.start();System.out.println(main thread ends);class FactorialThread extends Thread private int num;public FactorialThread(int num)this.num=num;,13,public void run()int i=num;int result=1;System.out.println(new thread started);while(i0)result=result*i;i=i-1;System.out.println(The factorial of+num+is+

9、result);System.out.println(new thread ends);运行结果main thread startsmain thread endsnew thread startedThe factorial of 10 is 3628800new thread ends,Thread类(续),14,结果说明main线程已经执行完后,新线程才执行完main函数调用thread.start()方法启动新线程后并不等待其run方法返回就继续运行(执行(),thread.run函数在一边独自运行,不影响原来的main函数的运行源程序修改如果启动新线程后希望主线程多持续一会再结束,可

10、在start语句后加上让当前线程(main)休息1毫秒的语句:try Thread.sleep(1);catch(Exception e);,Thread类(续),15,修改后运行结果main thread startsnew thread staredThe factorial of 10 is 3628800new thread endsmain thread ends运行结果说明新线程结束后main线程才结束例子 Ex8_1.java,Thread类(续)例8_1修改后运行结果,16,Thread类(续)常用API函数,17,Thread类(续)常用API函数,18,Thread类(续)

11、常用API函数,19,创建3个新线程,每个线程睡眠一段时间(06秒),然后结束public class Ex8_2 public static void main(String args)/创建并命名每个线程 TestThread thread1=new TestThread(thread1);TestThread thread2=new TestThread(thread2);TestThread thread3=new TestThread(thread3);(Starting threads);thread1.start();/启动线程1 thread2.start();/启动线程2 t

12、hread3.start();/启动线程3(Threads started,main endsn);,Thread类(续)例8_2,20,class TestThread extends Thread private int sleepTime;public TestThread(String name)super(name);/调用父类构造函数为线程命名 sleepTime=(int)(Math.random()*6000);public void run()try(getName()+going to sleep for+sleepTime);Thread.sleep(sleepTime)

13、;/线程休眠 catch(InterruptedException exception);(getName()+finished,Thread类(续)例8_2,21,运行结果Starting threadsThreads started,main endsthread1 going to sleep for 3519thread2 going to sleep for 1689thread3 going to sleep for 5565thread2 finishedthread1 finishedthread3 finished说明由于线程3休眠时间最长,所以最后结束,线程2休眠时间最短,

14、所以最先结束每次运行,都会产生不同的随机休眠时间,所以结果都不相同,Thread类(续)例8_2运行结果,22,Runnable接口,Runnable接口在编写复杂程序时相关的类可能已经继承了某个基类,而Java不支持多继承,在这种情况下,便需要通过实现Runnable接口来生成多线使用Thread创建线程对象时,通常使用的构造方法是:Thread(Runnable target)该构造方法中的参数是一个Runnable类型的接口,因此,在创建线程 对象时必须向构造方法的参数传递一个实现Runnable接口类的实例,该实例对象称作所创线程的目标对象.当线程调用start方法后,一旦轮到它来享用

15、CPU资源,目标对象就会自动调用接口中的run方法(接口回调).对于使用同一目标对象的线程,目标对象的成员变量自然就是这些线程的共享数据单元不同run()方法中的局部变量互不干扰。修改Ex8_1.java 例子:workspace/ThreadDemo2_2.java(java2006/example9/ThreadDemo1_1,ThreadDemo2_2)例子:Example9_3.java,TestThread extends Thread.,TestThread implements Runnable new Thread(Runnable th).start,23,使用Runnabl

16、e接口实现Ex8_1功能(使用一个目标对象)public class Ex8_1 public static void main(String args)System.out.println(main thread starts);FactorialThread t=new FactorialThread(10);new Thread(t).start();/创建Thread对象 System.out.println(new thread started,main thread ends);,Runnable接口(续)例8_1_1,24,class FactorialThread implem

17、ents Runnable private int num;public FactorialThread(int num)this.num=num;public void run()int i=num;int result=1;while(i0)result=result*i;i=i-1;System.out.println(The factorial of+num+is+result);System.out.println(new thread ends);,Runnable接口(续)例8_3,25,使用Runnable接口实现例8_2功能(使用不同目标对象)public class Ex8

18、_4 public static void main(String args)TestThread thread1=new TestThread();TestThread thread2=new TestThread();TestThread thread3=new TestThread();(Starting threads);new Thread(thread1,Thread1).start();new Thread(thread2,Thread2).start();new Thread(thread3,Thread3).start();(Threads started,main ends

19、n);,Runnable接口(续)例8_4,26,class TestThread implements Runnable private int sleepTime;public TestThread()sleepTime=(int)(Math.random()*6000);public void run()try(Thread.currentThread().getName()+going to sleep for+sleepTime);Thread.sleep(sleepTime);catch(InterruptedException exception);(Thread.current

20、Thread().getName()+finished);,Runnable接口(续)例8_4,Starting threadsThreads started,main endsThread2 going to sleep for 1498Thread1 going to sleep for 4544Thread3 going to sleep for 3251Thread2 finishedThread3 finishedThread1 finished,27,线程间的数据共享,代码共享多个线程的执行代码来自同一个类的run方法时,即称它们共享相同的代码数据共享当共享访问相同的对象时,即它们

21、共享相同的数据不同线程的run()方法中的局部变量互不干扰(Example9_5_1.java)使用Runnable接口可以轻松实现多个线程共享相同数据,只要用同一个实现了Runnable接口的实例作为参数创建多个线程即可(Ex8_5.java,p233-例9.3),class Example9_5_1 public static void main(String args)Move move=new Move();new Thread(move,zhangsan).start();new Thread(move,lisi).start();,class Move implements Run

22、nable/int i=0;public void run()int i=0;while(i=5)if(Thread.currentThread().getName().equals(zhangsan)i=i+1;System.out.println(Thread.currentThread().getName()+线程的局部变量i=+i);else if(Thread.currentThread().getName().equals(lisi)i=i+1;System.out.println(Thread.currentThread().getName()+线程的局部变量i=+i);try

23、Thread.sleep(800);catch(InterruptedException e),30,修改例8_4,只用一个Runnable类型的对象为参数创建3个新线程。public class Ex8_5 public static void main(String args)TestThread threadobj=new TestThread();(Starting threads);new Thread(threadobj,Thread1).start();new Thread(threadobj,Thread2).start();new Thread(threadobj,Threa

24、d3).start();(Threads started,main endsn);,线程间的数据共享(续)例8_5,31,class TestThread implements Runnable private int sleepTime;public TestThread()sleepTime=(int)(Math.random()*6000);public void run()try(Thread.currentThread().getName()+going to sleep for+sleepTime);Thread.sleep(sleepTime);catch(Interrupted

25、Exception exception);(Thread.currentThread().getName()+finished);,线程间的数据共享(续)例8_5,32,运行结果Starting threadsThread1 going to sleep for 966Thread2 going to sleep for 966Threads started,main endsThread3 going to sleep for 966Thread1 finishedThread2 finishedThread3 finished说明因为是用一个Runnable类型对象创建的3个新线程,这三个

26、线程就共享了这个对象的私有成员sleepTime,在本次运行中,三个线程都休眠了966毫秒,线程间的数据共享(续)例8_5运行结果,33,public class Ex8_6public static void main(String args)SellTickets t=new SellTickets();new Thread(t).start();new Thread(t).start();new Thread(t).start();,线程间的数据共享(实际应用)例8_6,用三个线程模拟三个售票口,总共出售200张票用3个线程模仿3个售票口的售票行为这3个线程应该共享200张票的数据,34

27、,class SellTickets implements Runnableprivate int tickets=200;public void run()while(tickets0)(Thread.currentThread().getName()+is selling ticket+tickets-);,线程间的数据共享(续)例8_6,35,运行结果选最后几行如下Thread-2 is selling ticket 6Thread-1 is selling ticket 5Thread-0 is selling ticket 4Thread-2 is selling ticket 3T

28、hread-1 is selling ticket 2Thread-0 is selling ticket 1说明在这个例子中,创建了3个线程,每个线程调用的是同一个SellTickets对象中的run()方法,访问的是同一个对象中的变量(tickets)如果是通过创建Thread类的子类来模拟售票过程,再创建3个新线程,则每个线程都会有各自的方法和变量,虽然方法是相同的,但变量却是各有200张票,因而结果将会是各卖出200张票,和原意就不符了,线程间的数据共享(续)例8_6运行结果,36,Thread类(续)常用API函数,interrupt()方法举例,public class Examp

29、le9_9 public static void main(String args)A a=new A();();();class A implements Runnable Thread student,teacher;A()teacher=new Thread(this);student=new Thread(this);teacher.setName(王教授);student.setName(张三);,38,public void run()if(Thread.currentThread()=student)try System.out.println(student.getName()

30、+正在睡觉,不听课);Thread.sleep(1000*60*60);catch(InterruptedException e)System.out.println(student.getName()+被老师叫醒了);System.out.println(student.getName()+开始听课);,39,else if(Thread.currentThread()=teacher)for(int i=1;i=3;i+)(上课!);try Thread.sleep(500);catch(InterruptedException e)student.interrupt();/吵醒stude

31、nt,40,GUI线程Example9_11,41,多线程的同步控制,有时线程之间彼此不独立、需要同步(相互配合)线程间的互斥同时运行的几个线程需要共享一个(些)数据一个线程对共享的数据进行操作时,不允许其他线程打断它,否则会破坏数据的完整性。即被多个线程共享的数据,在某一时刻只允许一个线程对其进行操作“生产者/消费者”问题(工资管理员/雇员)生产者产生数据,消费者消费数据,具体来说,假设有一个Java应用程序,其中有一个线程负责往数据区写数据,另一个线程从同一数据区中读数据,两个线程可以并行执行(类似于流水线上的两道工序)如果数据区已满,生产者要等消费者取走一些数据后才能再放;而当数据区没有

32、数据时,消费者要等生产者放入一些数据后再取,42,线程同步的概念,包括互斥和协作互斥:许多线程在同一个共享数据上操作而互不干扰,同一时刻只能有一个线程访问该共享数据。因此有些方法或程序段在同一时刻只能被一个线程执行,称之为监视区(临界区)协作:多个线程可以有条件地同时操作共享数据。执行监视区代码的线程在条件满足的情况下可以允许其它线程进入监视区(临界区),多线程的同步控制(续)线程同步(Synchronization),43,Producer and Consumer,Holder,44,synchronized 线程同步关键字把需要修改数据的方法用关键字synchronized来修饰,用于指

33、定需要同步的代码段或方法,也就是监视区当一个线程A使用一个synchronized修饰的方法时,其他线程想使用这个方法时就必须等待,直到线程A 使用完该方法(除非线程A使用wait主动让出CUP资源)当被synchronized限定的代码段执行完,就释放锁旗标(信号量)例子:Example9_13.java,多线程的同步控制(续)synchronized关键字,45,线程间的通信,为了更有效地协调不同线程的工作,需要在线程间建立沟通渠道,通过线程间的“对话”来解决线程间的同步问题 类的一些方法为线程间的通讯提供了有效手段一个线程在使用的同步方法中时,可能根据问题的需要,必须使用wait()(挂

34、起)方法使本线程等待,暂时让出CPU的使用权,并允许其它线程使用这个同步方法。其它线程如果在使用这个同步方法时如果不需要等待,那么它用完这个同步方法的同时,应当执行notify(),notifyAll()(恢复)方法通知所有的由于使用这个同步方法而处于等待的线程结束等待。例子:Example9_14.java,Ex8_8.java,Ex8_9.java,46,线程的优先级每个Java线程都有一个优先级,其范围都在1和10之间。默认情况下,每个线程的优先级都设置为5在线程A运行过程中创建的新的线程对象B,初始状态具有和线程A相同的优先级如果A是个后台线程,则B也是个后台线程可在线程创建之后的任何

35、时候,通过setPriority(int priority)方法改变其原来的优先级,线程的优先级(续),47,基于线程优先级的线程调度具有较高优先级的线程比优先级较低的线程优先执行对具有相同优先级的线程,Java的处理是随机的底层操作系统支持的优先级可能要少于10个,这样会造成一些混乱。因此,只能将优先级作为一种很粗略的工具使用。最后的控制可以通过明智地使用yield()函数来完成我们只能基于效率的考虑来使用线程优先级,而不能依靠线程优先级来保证算法的正确性,线程的优先级(续),48,假设某线程正在运行,则只有出现以下情况之一,才会使其暂停运行一个具有更高优先级的线程变为就绪状态(Ready)

36、;由于输入/输出(或其他一些原因)、调用sleep、wait、yield方法使其发生阻塞;对于支持时间分片的系统,时间片的时间期满,线程的优先级(续),49,创建两个具有不同优先级的线程,都从1递增到400000,每增加50000显示一次public class Ex8_13 public static void main(String args)TestThread runners=new TestThread2;for(int i=0;i 2;i+)runnersi=new TestThread(i);runners0.setPriority(2);/设置第一个线程优先级为2 runner

37、s1.setPriority(3);/设置第二个线程优先级为3 for(int i=0;i 2;i+)runnersi.start();,线程的优先级(续)例8_13,50,class TestThread extends Threadprivate int tick=1;private int num;public TestThread(int i)this.num=i;public void run()while(tick 400000)tick+;if(tick%50000)=0)/每隔5000进行显示(Thread#+num+,tick=+tick);yield();/放弃执行权,线程

38、的优先级(续)例8_13,51,线程的优先级(续)例8_13运行结果,运行结果Thread#1,tick=50000Thread#1,tick=100000Thread#1,tick=150000Thread#1,tick=200000Thread#1,tick=250000Thread#1,tick=300000Thread#1,tick=350000Thread#1,tick=400000Thread#0,tick=50000Thread#0,tick=100000Thread#0,tick=150000Thread#0,tick=200000Thread#0,tick=250000Thr

39、ead#0,tick=300000Thread#0,tick=350000Thread#0,tick=400000,结果说明具有较高优先级的线程1一直运行到结束,具有较低优先级的线程0才开始运行虽然具有较高优先级的线程1调用了yield方法放弃CPU资源,允许线程0进行争夺,但马上又被线程1抢夺了回去,所以有没有yield方法都没什么区别,52,如果在yield方法后增加一行sleep语句,让线程1暂时放弃一下在CPU上的运行,哪怕是1毫秒,则线程0也可以有机会被调度。修改后的run方法如下public void run()while(tick 400000)tick+;if(tick%500

40、00)=0)(Thread#+num+,tick=+tick);yield();try sleep(1);catch(Exception e);,线程的优先级(续)例8_13修改,53,线程的优先级(续)例8_13修改后运行结果,运行结果Thread#1,tick=50000Thread#1,tick=100000Thread#1,tick=150000Thread#1,tick=200000Thread#0,tick=50000Thread#1,tick=250000Thread#1,tick=300000Thread#0,tick=100000Thread#1,tick=350000Thr

41、ead#1,tick=400000Thread#0,tick=150000Thread#0,tick=200000Thread#0,tick=250000Thread#0,tick=300000Thread#0,tick=350000Thread#0,tick=400000,说明具有较低优先权的线程0在线程1没有执行完毕前也获得了一部分执行,但线程1还是优先完成了执行通常,我们在一个线程内部插入yield()语句,这个方法会使正在运行的线程暂时放弃执行,这是具有同样优先级的线程就有机会获得调度开始运行,但较低优先级的线程仍将被忽略不参加调度,54,本章小结,本章内容线程的基础知识线程的生命周期

42、线程的优先级本章要求了解线程的概念学会如何通过Thread类和Runnable接口创建线程,如何实现多线程的资源共享和通信,及如何控制线程的生命掌握线程同步的方法理解线程优先级的概念,以及基于优先级的线程调度,55,/holder hold ingergers class HoldInteger int shareInt=-1;boolean moreData=true;public void setSharedInt(int val)shareInt=val;public int getSharedInt()return shareInt;public void setMoreData(bo

43、olean b)moreData=b;public boolean hasMoreData()return moreData;,56,/producer produces integers and put them into holderclass ProducInteger extends ThreadHoldInteger pHold;public ProducInteger(HoldInteger h)pHold=h;public void run()for(int count=0;count10;count+)/sleep for random intervaltry Thread.s

44、leep(int)(Math.random()*300);catch(InterruptedException e)System.err.println(e.toString();pHold.setSharedInt(count);System.out.println(Prodecer set shareInt to+count);/setMoreData to false when count10.It means holder is fullpHold.setMoreData(false);,57,/consumer get integers from holderclass Consum

45、eInteger extends ThreadHoldInteger cHold;public ConsumeInteger(HoldInteger h)cHold=h;public void run()int val;while(cHold.hasMoreData()/sleep for random intervaltry Thread.sleep(int)(Math.random()*3000);catch(InterruptedException e)System.err.println(e.toString();val=cHold.getSharedInt();System.out.

46、println(Consumer retrieved+val);,58,public class synDemo public static void main(String args)HoldInteger h=new HoldInteger();ProducInteger p=new ProducInteger(h);ConsumeInteger c=new ConsumeInteger(h);p.start();c.start();,返回,59,运行结果Prodecer set shareInt to 0Prodecer set shareInt to 1Prodecer set sha

47、reInt to 2Prodecer set shareInt to 3Prodecer set shareInt to 4Consumer retrieved4Prodecer set shareInt to 5Prodecer set shareInt to 6Prodecer set shareInt to 7Prodecer set shareInt to 8Prodecer set shareInt to 9Consumer retrieved9,返回,60,挂起 有时候两个线程并不是同步的,即不涉及都需要调用一个同步方法,但线程也可能需要暂时的挂起。所谓挂起一个线程就是让线程暂时让

48、出CPU的使用权限,暂时停止执行,但停止执行的持续时间不确定,因此不能使用sleep方法暂停线程。挂起一个线程需使用wait方法,即让准备挂起的线程调用 wait 方法,主动让出CPU的使用权限.恢复 为了恢复该线程,其它线程在占有CUP资源期间,让挂起的线程的目标对象执行notifyAll()方法,使得挂起的线程继续执行;如果线程没有目标对象,为了恢复该线程,其它线程在占有CUP资源期间,让挂起的线程调用notifyAll()方法,使挂起的线程继续执行。,返回,61,Example9_13.java部分代码 public synchronized void 存取(int number)/存取

49、方法 if(Thread.currentThread()=会计)text1.append(今天是星期+weekDay+n);for(int i=1;i=3;i+)money=money+number;try Thread.sleep(1000);catch(InterruptedException e)text1.append(帐上有+money+万n);,为什么在会计让出CPU后,出纳仍然不能运行存取()方法?,当会计让出CPU后,出纳启动线程调用run()方法,但会计在调用存取()方法时使用了synchronized,62,class Example9_5 public static vo

50、id main(String args)Move move=new Move();();();class Move implements Runnable Thread zhangsan,lisi;Move()zhangsan=new Thread(this);zhangsan.setName(张三);lisi=new Thread(this);lisi.setName(李四);,63,public void run()int i=0;while(i=5)if(Thread.currentThread()=zhangsan)i=i+1;System.out.println(zhangsan.g

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号