第8章线程上课.ppt

上传人:sccc 文档编号:5931459 上传时间:2023-09-05 格式:PPT 页数:49 大小:1.01MB
返回 下载 相关 举报
第8章线程上课.ppt_第1页
第1页 / 共49页
第8章线程上课.ppt_第2页
第2页 / 共49页
第8章线程上课.ppt_第3页
第3页 / 共49页
第8章线程上课.ppt_第4页
第4页 / 共49页
第8章线程上课.ppt_第5页
第5页 / 共49页
点击查看更多>>
资源描述

《第8章线程上课.ppt》由会员分享,可在线阅读,更多相关《第8章线程上课.ppt(49页珍藏版)》请在三一办公上搜索。

1、第八章 线 程,8.1 线程和多线程8.2 线程的生命周期8.3 线程的优先级与调度管理8.4 创建线程 8.5 创建线程8.6 线程的常用方法8.7 同步问题8.8 线程交互8.9 挂起、恢复和终止线程8.10 线程联合8.11 线程守护,目 录,进程与线程的区别,进程(Process)进程就是程序的一次执行活动运行一个程序,就是启动了一个进程线程(Thread)线程是一个程序内部的一条执行路径或一个控制流线程称为处理器调度的对象,而进程是资源分配的对象,8.1 进程与线序的区别,线程和进程的区别每个进程都有独立的代码和数据空间(进程上下文),进程间的切换开销大。同一进程内的多个线程共享相同

2、的代码和数据空间,可以实现线程间的通信和同步操作每个线程有独立的运行栈和程序计数器(PC),线程间的切换开销小,8.1 Java中的线程,JVM启动“主线程”执行主类的main()方法在main()方法中可以再创建线程JVM在主线程和其他线程之间轮流切换JVM等到主线程及主线程中的所有线程都结束之后,才结束该Java应用程序,8.2 线程的生命周期,线程在它的一个完整的生命周期中通常要经历6种状态JDK中用Thread.State枚举表示出了这6种状态:NEW:至今尚未启动的线程处于这种状态。称之为“新建”状态。RUNNABLE:正在 Java 虚拟机中执行的线程处于这种状态。称之为“可运行”

3、状态。BLOCKED:受阻塞并等待某个监视器锁的线程处于这种状态。称之为“阻塞”状态。WAITING:无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。称之为“等待”状态。TIMED_WAITING:等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。称之为“超时等待”状态。TERMINATED:已退出的线程处于这种状态。称之为“中止”状态。,1.新线程2.可运行的线程3.被阻塞和等待状态下的线程4.被终止的线程,线程的状态,实例分析 线程的状态,例子1,阐述线程的4种状态。分析结果JVM首先将CPU资源给主线程。在“主线程”和“zhang线程”之间切换。在“zhang线

4、程”和“wang线程”之间切换。线程进入死亡状态,JVM结束整个进程。说明:上述程序在不同的计算机运行或在同一台计算机反复运行的结果不尽相同,输出结果依赖当前CPU资源的使用情况。例子2,主动调用sleep()方法让出CPU的使用权。,8.4&8.5 创建线程,方法一:实现Runnable接口方法二:继承Thread类两种方法的讨论,方法一:继承Thread类,Thread类本身也实现了Runnable接口,但run()方法中没有任何操作语句Thread类的默认构造函数将它自己作为默认目标在继承Thread类的子类中覆盖run()方法,创建该子类的对象,从而创建一个线程,方法一:继承Threa

5、d类,public class myThread extends Threadpublic void run()/线程体,整个线程的核心while(running)sleep(100);public static void main(String args)Thread t1=new myThread();/无参数 Thread t2=new myThread(“name”);t1.start();例子3,运用了System类中的类方法:exit(int n),结束整个程序。,方法二:实现Runnable接口,是Java中用以实现线程的接口。Runnable接口允许我们可以将任何对象作为一个线

6、程的目标Runnable接口中只定义了一个方法就是run()方法,也就是线程体。,public interface Runnablepublic abstract void run(),public class xyz implements Runnableint i;public void run()while(true)System.out.println(Hello+i+);,方法二:实现Runnable接口,用实现Runnable接口对象构造一个线程的方法如下:Thread t=new Thread(runnable 对象);说明:Thread第二种构造方法中包含有一个Runnable

7、实例的参数,必须定义一个实现Runnable接口的类并产生一个该类的实例,该实例称为所创线程的目标对象。例子4,两个线程zhang和cheng,使用同一目标对象。更常见的作法是由一个对象来处理它自己的线程 例子:Animation,Runnable r=new xyz();Thread t=new Thread(r);,方法二:实现Runnable接口,使用同一目标对象的线程可共享目标对象的成员变量利用成员变量实现线程间的同步和通信run()方法中的局部变量是线程的局部变量不同线程的局部变量互不干扰,两种方法的讨论,适用于采用实现Runnable接口方法的情况因为Java只允许单继承,如果一个

8、类已经继承了Thread,就不能再继承其他类。如:public class mythread extends Applet implements Runnable面向对象设计原则,使用适配器类(adapter class),使用内部类作为适配器将某个类与某个接口相捆绑在一个类A中创建一个实现了Runnable的匿名内部类B,类B可以调用A的任意一个方法作为线程体,如同有方法指针一样避免了在应用代码中实现一个通用的run()方法例子:Animation2 Animation3,线程的启动,必须通过方法start()来启动线程,此时java将直接调用run()方法,start()方法也在Threa

9、d类中。t.start();start()方法在线程的生命周期中只能被调用一次,否则将发生IllegalThreadStateException,8.3 线程的优先级与调度管理,一个CPU在同一时间只能分配给一个线程做一件事。优先级:10个级别,用Thread类中的类常量表示。级别1:Thread.MIN_PRIORITY 最低优先级级别5:Thread.NORM_PRIORITY 默认优先级级别10:Thread.MAX_PRIORITY 最高优先级通过setPriority(int i)设置线程的优先级 例子:Priority并不是每个操作系统都会按照优先级来选择线程,有些操作系统并不看这

10、个优先级为提高程序的可移植性,不建议手工调整线程的优先级,8.6 线程的常用方法,start()启动线程只能在生命周期中被调用一次调用后,线程不一定立即执行,而需要看CPU是否调度到该线程run()系统自动调用,线程体(线程被调度后执行的操作)。sleep(longmillis)(或Thread.sleep())它用于暂时停止一个线程的执行。在休眠期间被打断抛出InterruptedException(必须捕获)public static void sleep(long millis,int nanos),8.6 线程的常用方法,isAlive()检查线程是否为活动状态,如果线程已经启动且尚未

11、终止,则为活动状态。活动状态不意味着这个线程正在执行。currentThread()类方法,返回当前使用CPU的线程。interrupt()中断线程,使其从休眠状态进入可执行的状态。setName(String s),getName()设置和返回线程的名字。public Thread.State getState()返回该线程的状态。join()将引起现行线程等待,直至调用方法join的线程结束。如:thread2.join();让当前线程thread1等待yield()线程让步将执行的权力交给其它线程,自己到队列的最后等待。,线程中断-interrupt(),例8-8,线程让步-yield(

12、),线程联合-join(),有时需要线程间的接力来完成某项任务,或希望主线程最后退出,可以在main中实现一个足够长的sleep(),但不是一个好方法join()方法可以使两个交叉执行的线程变成顺序执行,8.7 线程同步,多线程的应用程序中,两个或多个线程需要共享对同一个数据的访问。被多线程并发访问的对象我们管它叫临界资源。JVM执行代码时不是一条指令就完成了,多线程并发访问临界资源,如果不加同步控制,打断了原子操作,会造成临界资源的数据冲突数据不一致数据不完整,1 问题的提出,多线程访问临界资源的问题:栈售票系统例8-9 记账问题,问题的提出-栈,class Stack int idx=0;

13、char data=new char6;public void push(char c)dataidx=c;idx+;public char pop()idx-;return dataidx;,问题的提出-售票系统,问题的提出-记账问题,线程同步,同步机制对象的“锁定标志”同步化方式:同步块(synchronized block)同步方法(synchronized method),2 对象的锁定标志,Java可以为每一个类和每一个对象的实例配有一个标志,这个标志称做“对象锁”(“锁定标志”)。如果某个线程获得了某个类或对象的锁,在其释放锁之前,其他线程都不能获取该锁了当线程执行用synchro

14、nized 修饰的代码时,表明线程需要获取某个对象的锁,如果不能得到锁,线程进入阻塞状态一直等待锁的释放。当持有锁的线程运行完synchronized()包含的程序块后,这个锁将会被自动返还。,2 对象的锁定标志,每一个对象不仅仅有个锁标记,他还有一个锁池,当一个线程希望获得对象的锁标记,却拿不到的时候,这个线程会进入该对象的锁池,3 同步方法,同步化方法形式如下:synchronized void f()如果f是实例方法,线程需要请求当前对象实例的锁如果f是静态方法,线程需要请求当前类的锁可以同步一个类中多个方法,即同一时间仅运行其中一个方法 SpreadSheet注意:如果一个对象被多个线

15、程访问,所有读写该对象的成员变量的方法都需要同步。但是final变量可以不用,因为不能修改,同步块,synchronized 关键字还可以用以保护任意的代码块 synchronized(object)代码块 object是被同步对象的引用只有获得object的锁标记的线程才能进入代码块当代码块执行结束后,object的锁标记就会被自动释放注意 同步代码块锁的是对象,而不是代码块当一个线程失去CPU,进入可运行状态,或者进入阻塞状态时候,不会释放拥有的锁标记,同步方法等价于:public void push(char c)synchronized(this),程序改进,class Stackin

16、t idx=0;char data=new char6;,public char pop()synchronized(this)idx-;return dataidx;,public void push(char c)synchronized(this)/this表示Stack的当前对象 dataidx=c;idx+;,死锁,一个对象的锁标记只能给一个线程,但一个线程完全可以同时拥有多个对象的锁标记,即同步代码块可以嵌套synchronized(o1)/拿到o1的锁标记synchronized(o2)/这个线程同时拥有o1和o2对象的锁标记当一个线程阻塞在锁池中的时候,不会释放拥有的其他锁标记

17、,多个线程常常被创建用来完成不相关的任务,而线程之间有一些交互。为什么两个线程可能需要交互呢?-线程间的合作例子:有两个人,一个在刷盘子,另一个在烘干。这两个人各自代表一个线程,他们之间有一个共享的对象碗橱。,8.8 在同步方法中使用wait()、notify()、notifyAll(),解决方法,类java.lang.Object中定义了三个final方法:wait(),notify(),notifyAll(),如果一个线程需要等待应用中另一个事件发生,就可以使用wait()放弃对锁的拥有权调用notify(),java会按任意条件选择多个等待的线程中的一个唤醒通过某个对象调用以上方法的线程

18、必须要拥有对象的锁,只能在有synchronized的代码块中才能使用,否则会有IllegalMonitorStateException,解决方法,Java中的每个对象实例都有两个线程队列和它相连。第一个用来排列等待锁定标志的线程-锁池第二个则用来实现wait()和notify()的等待队列。,wait()必须出现在同步代码中线程会释放所拥有的所有的锁标记,而且肯定至少有一个锁被释放线程被中断运行,退出运行状态,进入对象的等待队列等待队列是一个阻塞的状态notify()必须出现在同步代码中从当前的等待队列中释放一个线程(notifyAll为释放所有线程),使之成为可运行的线程,一般会用noti

19、fyAll不会释放自己拥有的对象的标记锁唤醒后的线程并不自动接受锁,而是和其他线程竞争锁,notifyAll()会把所有的线程都从等待队列中释放出来,出来后的线程进入什么状态?进入锁池状态无论状态如何转变,都不能改变程序执行的流程,所以从等待队列中释放出来的线程还是得得到锁标记才能执行wait后面的代码,所以它只能回到锁池中等待对象的锁标记,notifyAll(),等待队列和锁池的区别,线程要获得锁标记而得不到是进锁池,是迫不得已的等待队列中的线程是主动进入的锁池中的线程手里还可能有锁标记等待队列中的线程手里一定没有锁标记等待队列中的线程如果没有调notify的话,永远出不去,只有当调用了notify后,才会出去,传递消息综合应用实例,现在来建立一个经典交互,即生产者/消费者问题的实际例子。演示1,8.11 守护线程,线程调用setDaemon方法可以将自己设置成一个守护(Daemon)线程(后台线程)void setDaemon(boolean on)thread.setDaemon(true);线程默认是非守护线程,非守护线程也称作用户(user)线程当程序中的所有用户线程都已结束运行时,即使守护线程的run方法中还有需要执行的语句,守护线程也立刻结束运行必须在运行之前设置自己是否是守护线程,

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

当前位置:首页 > 建筑/施工/环境 > 农业报告


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号