第七章 java的线程

上传人:门**** 文档编号:243126306 上传时间:2024-09-16 格式:PPT 页数:85 大小:563KB
返回 下载 相关 举报
第七章 java的线程_第1页
第1页 / 共85页
第七章 java的线程_第2页
第2页 / 共85页
第七章 java的线程_第3页
第3页 / 共85页
点击查看更多>>
资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,第七章 线程,本章内容,7.1,线程的概念模型,7.2,线程体,7.3,线程的状态,7.4,线程体的构造,7.5,线程的调度,7.6,基本的线程控制,7.7,多线程互斥与同步,7.1,线程的概念模型,1,几个相关概念,1,“,程序”代表一个静态的对象,是内含指令和数据的文件,存储在磁盘或其他存储设备中,2,“,进程”代表一个动态的对象,是程序的一个执行过程,存在于系统的内存中。一个进程对应于一个程序,3,“,线程”是运行于某个进程中,用于完成某个具体任务的顺序控制流程,有时被称为轻型进程。,2,进程,进程本身可以看成是系统资源和程序代码的执行位置的集合。用过,UNIX,操作系统的读者都知道,在,UNIX,操作系统中,每个应用程序的执行都在操作系统内核中登记一个进程标志,操作系统根据分配的标志对应用程序的执行进行调度和系统资源分配。每个进程都有自己的内存单元,进程之间是互相独立的,一个进程一般不允许访问其他进程的内存空间,因此,进程间通信非常困难。,什么是线程,一个线程是一个程序内部的顺序控制流,也被称为,“,轻型进程,(,lightweight process,),”,或,“,执行上下文,(,execution context,),”,线程是比进程更小的执行单位。如果将进程概念一分为二,则进程中的系统资源,可以看成是一个静态的对象;而程序代码的执行位置,可以看成一个动态对象,这个动态的部分就是线程。,图,10.2,每个线程都有其自己的堆栈和程序计数器,(PC),。用户可以把程序计数器,(PC),设想为用于跟踪线程正在执行的指令,而堆栈用于跟踪线程的上下文,(,上下文是当线程执行到某处时,当前的局部变量的值,),。虽然用户可以编写出在线程之间传送数据的子程序,但在正常情况下,一个线程不能访问另外一个线程的栈变量。,3,线程的概念模型,一个线程,(,或称执行上下文,),由三个主要部分组成:, 一个虚拟处理机(,CPU,),CPU,执行的代码, 代码操作的数据,虚拟的,CPU,,封装在,java.lang.Thread,类的一个实例里。构造线程时,定义其上下文的代码和数据是由传递给它的构造函数的对象指定的。,CPU,所执行的代码,是由传递给它的构造函数的对象指定的,传递给,Thread,类。,代码可以或不可以由多个线程共享,这和数据是独立的。两个线程如果执行同一个类的实例代码,则它们可以共享相同的代码。,CPU,所处理的数据,传递给,Thread,类。类似地,数据可以或不可以由多个线程共享,这和代码是独立的。两个线程如果共享对一个公共对象的存取,则它们可以共享相同的数据。,7.2,线程体,1,线程体,java,的线程是通过,java.lang.Thread,类,来实现的。,每个线程都是通过某个特定,Thread,对象的方法,run( ),来完成其操作的,方法,run( ),称为线程体。,2,线程的作用,线程真正的神奇之处并不在于它处理顺序任务流程的作用,而是在于多个线程可以同时运行,并且在一个程序内执行不同的任务。,线程和进程比较,每个进程都有独立的代码和数据空间,(,进程上下文,),,进程切换的开销大。,线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器,(PC),,线程切换的开销小。,多进程:在操作系统中,能同时运行多个任务,(,程序,),。,多线程:在同一应用程序中,有多个顺序流同时执行。,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,线程之间的通信比较容易解决,从而极大地提高了程序的运行效率,.,3 Java,的多线程,从字面上来解释,多线程就是多个线程的集合。具体地讲,多线程就是同时执行一个以上的线程,一个线程的执行不必等待另一个线程执行完才执行,所有线程都可以发生在同一时刻。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配,.,在多线程程序中,多个线程共享内存,从而极大地提高了程序的运行效率,.,7.3,线程的状态,线程的状态,创建状态(,new,):线程对象已经创建,但尚未启动,所以不可运行。,可运行状态(,runnable),:所有资源都准备好,就差,CPU,资源,一旦线程调度器分配,CPU,资源给该线程,立刻开始执行。,死亡状态(,dead,):线程体执行完毕,即,run( ),方法运行结束。,堵塞状态(,blocked,):不仅缺乏,CPU,资源,还缺乏其他资源,线程的状态,new Thread,Runnable,Blocked,Dead,start( ),run( ),运行结束,yield( ),创建状态,(new Thread),Thread myThread = new MyThreadClass( );,(注意:,MyThreadClass,是,Thread,的子类),可运行状态,( Runnable ),Thread myThread = new MyThreadClass( );,myThread.start( );,阻塞状态(,Blocked,),调用了,sleep,()方法,;,为等候一个条件变量,线程调用,wait,()方法,;,输入输出流中发生线程阻塞,;,死亡状态(,Dead,),自然撤消(线程执行完),下面几种情况下,当前线程会放弃,CPU,,进入阻塞状态(,blocked):,线程调用,sleep(),方法主动放弃;,由于当前线程进行,I/O,访问,外存读写,等待用户输入等操作,导致线程阻塞;,为等候一个条件变量,线程调用,wait,()方法;,线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。,线程状态的转换条件,可运行状态和不可运行状态转换条件,进入不可运行状态条件,返回可运行状态条件,挂起(调用,suspend(),方法),调用,resume(),方法,睡眠(调用,sleep(),方法),sleep(),方法的指定时间结束,阻塞(请求,I/O,操作),I/O,操作结束,等待(调用某对象的,wait(),方法),调用该对象的,notfiy(),或,notifyAll(),方法,停止线程,正常终止,run(),方法运行完毕,强行中止,调用线程对象的,stop(),方法,创建线程对象的上级线程停止,线程的生存周期,创建,消亡,运行,阻塞,就绪,休眠,等待,start(),sleep(),wait(),I/O,CPU,可用,任务完成,yield(),7.4,线程体的构造,如何创建线程,继承,Thread,类,定义线程的方法,实现,Runnable,接口,继承,Thread,类,在构造函数中调用父类的构造函数,参考,Thread,类的构造函数,在,run,方法中实现任务处理功能,创建线程对象后,通过调用,start(),方法启动线程,继承,Thread,类,class,PrintThread,extends,Thread ,private int,sleepTime;,public,PrintThread( String name ) ,super,( name );,sleepTime = (,int,) ( Math.random() *,5000,);,System.out.println(,Name: ,+ getName(),+,; sleep: ,+ sleepTime );,public void,run() ,try,System.out.println( getName() +, going to sleep,);,Thread.sleep( sleepTime );,catch,( InterruptedException ie ) ,System.err.println( ie.toString() );,System.out.println( getName() +, done sleeping,);,执行线程任务,public class,ThreadTester ,public static void,main( String args ),PrintThread thread1, thread2, thread3, thread4;,thread1 =,new,PrintThread(,thread1,);,thread2 =,new,PrintThread(,thread2,);,thread3 =,new,PrintThread(,thread3,);,thread4 =,new,PrintThread(,thread4,);,System.out.println(,nStarting threads,);,thread1.start();,thread2.start();,thread3.start();,thread4.start();,System.out.println(,Threads startedn,);,Name: thread1; sleep: 4464,Name: thread2; sleep: 1907,Name: thread3; sleep: 1149,Name: thread4; sleep: 143,Starting threads,thread1 going to sleep,thread3 going to sleep,Threads started,thread4 going to sleep,thread2 going to sleep,thread4 done sleeping,thread3 done sleeping,thread2 done sleeping,thread1 done sleeping,执行结果,实现,Runnable,接口,实现,run,方法,在该方法中实现任务处理功能,参考,Runnable,接口定义,创建实现,Runnable,接口的类对象,利用,Thread,类的构造函数创建线程对象,public Thread(Runnable target),通过调用线程对象的,start(),方法启动线程,实现,Runnable,接口,class,PrintRunnable,implements Runnable,private int name;,private int,sleepTime;,public,PrintRunnable( String name ) ,this.name = name;,sleepTime = (,int,) ( Math.random() *,5000,);,System.out.println(,Name: ,+ name,+,; sleep: ,+ sleepTime );,public void,run() ,try,System.out.println( name +, going to sleep,);,Thread.sleep( sleepTime );,catch,( InterruptedException ie ) ,System.err.println( ie.toString() );,System.out.println( name +, done sleeping,);,执行线程任务,public class,RunnableTester ,public static void,main( String args ),PrintRunnable thread1, thread2, thread3, thread4;,thread1 =,new,PrintRunnable (,thread1,);,thread2 =,new,PrintRunnable (,thread2,);,thread3 =,new,PrintRunnable (,thread3,);,thread4 =,new,PrintRunnable (,thread4,);,System.out.println(,nStarting threads,);,new Thread(thread1).start();,new Thread(thread2).start();,new Thread(thread3).start();,new Thread(thread4).start();,System.out.println(,Threads startedn,);,选择合适的方式创建线程,继承,Thread,类,简单直观,不能继承其他父类,实现,Runnable,接口,可继承其他父类,代码稍复杂,ClockApplet,示例,ClockApplet,示例,import java.applet.Applet;,import java.awt.Graphics;,import java.util.Date;,public class ClockApplet extends Applet implements Runnable ,private Thread clockThread = null;,public void start() ,if (clockThread =null) ,clockThread = new Thread(this);,clockThread.start();,public void paint(Graphics g) ,Date date = new Date();,g.drawString(date.toString(), 5, 10);,ClockApplet,示例,public void run() ,while (true) ,repaint();,try ,Thread.sleep(1000);, catch (InterruptedException e) ,7.5,线程的调度,线程的优先级,Java,线程优先级,范围从,1,10,,数字越高越能被优先执行,通过线程对象的,setPriority(),方法设置优先级,缺省优先级为,5,三个常数:,MAX_PRIORITY,、,MIN_PRIORITY,、,NORM_PRIORITY,获得和设计线程优先级,int getPriority();,void setPriority(int newPriority);,线程的调度,大多数计算机只有一个,CPU,,因此一个时刻只有一个线程在运行,多个线程只能是,“,宏观上并行,微观上串行,”,。,在有限个,CPU,的系统中确定多个线程的执行顺序称为线程的调度。,线程的调度,java,提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪些线程来执行。,Java,语言采用了简单的,“,固定优先级调度算法,”,,高优先级的线程总是更容易得到,CPU,使用权。,只有当所有高优先级的线程停止或处于不可运行状态,低优先级的线程才能运行。,线程的调度是抢先式的,按照优先级来调度:,时间片方式,非时间片方式,抢占式调度,Java,语言的“抢占式调度策略”,当一个线程正在运行时,如果有更高优先级的线程处于“可运行”状态,系统将强制暂停低优先级的线程,使高优先级的线程“抢占”低优先级线程的,CPU,资源,自私的线程,线程如果不放弃,CPU,,其他同优先级的线程就很难得到运行,run() ,while(true) ,/ yield();,除非更高优先级的抢占,CPU,,或者,时间片策略,部分平台(如,Windows,)针对“自私线程”采用了时间片轮换策略,将,CPU,的运行时间划分成段,相同优先级的线程分别占用一段轮流执行,执行的顺序和轮换的时间不可预测,线程调度队列(,Runnable,队列),优先级为,1,优先级为,5,优先级为,10,线程,1,线程,2,线程,3,。,。,线程,4,线程,5,class ThreadTest,public static void main( String args ) ,Thread t1 = new MyThread(T1);,t1.setPriority( Thread.MIN_PRIORITY );,t1.start( );,Thread t2 = new MyThread(T2);,t2.setPriority( Thread.MAX_PRIORITY );,t2.start( );,Thread t3 = new MyThread(T3);,t3.setPriority( Thread.MAX_PRIORITY );,t3.start( );,class MyThread extends Thread ,String message;,MyThread ( String message ) ,this.message = message;,public void run() ,for ( int i=0; i3; i+ ),System.out.println( message+ +getPriority() );,运行结果:,T2 10,T2 10,T2 10,T3 10,T3 10,T3 10,T1 1,T1 1,T1 1,注意:并不是在所有系统中运行,Java,程序时都采用时间片策略调度线程,所以一个线程在空闲时应该主动放弃,CPU,,以使其他同优先级(调用,yield( ),方法)和低优先级(调用,sleep( ),方法,),的线程得到执行。,正确理解优先级和调度,线程调度策略和,JRE,运行平台直接相关,Java,本身并不直接支持时间片,不要编写“自私的”线程,在适当的时候通过,yield(),方法放弃,CPU,yield(),方法使线程由运行状态变为就绪状态,yield(),方法放弃,CPU,后,只有同优先级的线程才能得到,CPU,,低优先级的线程仍不能运行,正确理解优先级和调度,在多任务环境中,如果完全依赖低优先级线程可能永远得不到运行机会,称为线程“饿死”,线程调度程序可能为低优先级线程分配,CPU,,以防止线程“饿死”,优先级只是用来提高多线程的调度效率,并不保证高优先级线程永远优先运行,不要依赖线程的优先级来设计对调度敏感的算法,正确理解优先级和调度,public class PriorityTest extends Thread ,public PriorityTest(String name) ,super(name);,public void run() ,while(true) ,int i;,for(int j=0; j100000; j+),for(int k=0; k1000; k+),i = j + k;,if (getPriority()NORM_PRIORITY),System.out.println(getName() + is running.);,正确理解优先级和调度,public static void main(String args) ,PriorityTest test1 = new PriorityTest(Higer priority thread);,PriorityTest test2 = new PriorityTest(Lower priority thread);,test2.setPriority(4);,test1.start();,test2.start();,从运行结果上看,低优先级的线程也有执行的机会。,7.6,基本的线程控制,基本的线程控制,终止线程,线程执行完其,run(),方法后,会自然终止。,测试线程状态,可以通过,Thread,中的,isAlive(),方法来获取线程是否处于活动状态;,线程由,start(),方法启动后,直到其被终止之间的任何时刻,都处于 ,Alive,状态。,线程的暂停和恢复,sleep(),方法,yield(),方法,调用该方法的线程把自己的控制权让出来,线程调度器把该线程放到同一优先级的,Runnable,队列的最后,然后从该队列中取出下一个线程执行。该方法是给同优先级的线程以执行的机会,如果同优先级的,Runnable,队列中没有其它线程,则该线程继续执行。,7.7,多线程互斥与同步,多线程互斥与同步,Stack,对象,s,线程,T1,class T1 extends Thread,Stack s;,public T1(Stack s),this.s=s;,public void run(),s.pop();,线程,T2,class T2 extends Thread,Stack s;,public T2(Stack s),this.s=s;,public void run(),s.pushi(char c);,多线程的互斥与同步,临界资源问题,class Stack,int idx=0;,char data = new char6;,public void push(char c),dataidx = c;,idx+;,public char pop(),idx-;,return dataidx;,main(),Stack mystack=new Stack();,T1 myT1=new T1(mystack);,T2 myT2=new T2(mystack);,myT1.start();,myT2.start();,两个线程,A,和,B,在同时使用,Stack,的同一个实例对象,,A,正在往堆栈里,push,一个数据,,B,则要从堆栈中,pop,一个数据。,1),操作之前,data = | p | q | | | | | idx=2,2) A,执行,push,中的第一个语句,将,r,推入堆栈;,data = | p | q | r | | | | idx=2,3)A,还未执行,idx+,语句,,A,的执行被,B,中断,,B,执行,pop,方法,返回,q,:,data = | p | q | r | | | | idx=1,4A,继续执行,push,的第二个语句:,data = | p | q | r | | , | | idx=2,最后的结果相当于,r,没有入栈。,产生这种问题的原因在于对共享数据访问的操作的不完整性。,在,Java,语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。,每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。,关键字,synchronized,来与对象的互斥锁联系。当某个方法用,synchronized,修饰时,表明该对象在任一时刻只能由一个线程访问。,对象没被访问时候,其锁是打开的;当对象被某个线程访问时,锁就被该线程关上,其他线程就无法访问该对象,直到该线程访问完毕打开锁。,public void push(char c),synchronized(this),dataidx=c;,idx+;,public char pop(),synchronized(this),idx-;,return dataidx;,线程,T1,、,T2,未访问,s,前,Stack,对象,s,线程,T1,class T1 extends Thread,Stack s;,public T1(Stack s),this.s=s;,public void run(),s.pop();,线程,T2,class T2 extends Thread,Stack s;,public T2(Stack s),this.s=s;,public void run(),s.pushi(char c);,线程,T1,拿到,s,的锁,关闭,Stack,对象,s,线程,T1,class T1 extends Thread,Stack s;,public T1(Stack s),this.s=s;,public void run(),s.pop();,线程,T2,class T2 extends Thread,Stack s;,public T2(Stack s),this.s=s;,public void run(),s.pushi(char c);,线程,T1,访问,s,结束,释放锁,Stack,对象,s,线程,T1,class T1 extends Thread,Stack s;,public T1(Stack s),this.s=s;,public void run(),s.pop();,线程,T2,class T2 extends Thread,Stack s;,public T2(Stack s),this.s=s;,public void run(),s.pushi(char c);,synchronized,除了象上面讲的放在对象前面限制一段代码的执行外,还可以放在方法声明中,表示整个方法为同步方法。,public synchronized void push(char c),如果,synchronized,用在类声明中,则表明该类中的所有方法都是,synchronized,的。,用于线程同步的三个函数,wait(),notify(),notifyAll(),(1) wait,nofity,notifyAll,必须在已经持有锁的情况下执行,所以它们只能出现在,synchronized,作用的范围内.,(2) wait,的作用:释放已持有的锁,进入,wait,队列.,(3) notify,的作用:唤醒,wait,队列中的第一个线程并把它移入锁申请队列.,(4) notifyAll,的作用:唤醒,wait,队列中的所有的线程并把它们移入锁申请队列.,多线程的同步,class SyncStack,private int index = 0;,private char buffer = new char6;,public synchronized void push(char c),while(index = buffer.length),try,this.wait();,catch(InterruptedException e),this.notify();,bufferindex = c;,index+;,public synchronized char pop(),while(index =0),trythis.wait();,catch(InterruptedException e),this.notify();,index- -;,return bufferindex;,生产者,-,消费者问题,class Producer implements Runnable,SyncStack theStack;,public Producer(SyncStack s),theStack = s;,public void run(),char c;,for(int i=0; i20; i+),c =(char)(Math.random()*26+A);,theStack.push(c);,System.out.println(Produced: +c);,tryThread.sleep(int)(Math.random()*1000);,catch(InterruptedException e),class Consumer implements Runnable,SyncStack theStack;,public Consumer(SyncStack s),theStack = s;,public void run(),char c;,for(int i=0;i 0.01),System.out.println(Current average is + avg);,old_avg = avg;,catch(IOException e),System.out.println(Error: + e); ,上面的例子是一个示范管道流的程序。它拥有一个在随机时间产生随机数的生产者线程;一个用于读取输入数据并不断地计算它们的平均值的过滤线程;一个在屏幕上输出结果的消费者线程。这个程序不会自动结束,用户可以通过,Ctrl+C,键来终止它。,谢谢!,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 小学资料


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!