版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第9章 Java多线程机制经常在编程遇到一个类似下列程序段的问题:class Apublic static void main(String args)while(true)我听朋友说话;while(true)我向朋友说话;可以将上面两个while放到两个线程中去独立执行如:线程一:while(true)我听朋友说话;线程二:while(true)我向朋友说话;想像一下,线程一和线程二不会互相等待,可以独立执行,互不干扰。这就是多线程编程。在Java中多线程编程要比Visual C+的简单。在Java中,使用JVM(Java虚拟机:Java Virtual Machine),来负责调度这些线程
2、,使得每个线程都有平等的机会获得CPU进行独立运行,并且,各个线程之间可以有联系,也可以没有联系,可以进行数据共享,也可以进行通信。9.1 Java中的多线程9.1.1 程序、进程与线程程序(Program):以数据、代码形式存放在磁盘上,它是死的,如果用户不去运行它,那么它就一直呆在那不动。进程(Process):如果开始运行一个程序,那么操作系统所做的工作有:在内存开辟一个不大的空间,并根据该程序的信息,组装成一个PCB(Process Control Block,进程控制块),里面有该进程的重要信息,并同时,将程序的代码和数据(可能是全部的,也可能是一部分)装入内存,然后,将该程序以进程
3、的插入到操作系统的一个就绪队列中去,这样,这个程序就可以有机会获得CPU开始运行,那么此时,程序就成为了进程。进程是活动的,有生命的,而程序是死的。线程(Thread):通常一个程序就形成一个进程,可是进程可能会太庞大了,且如果以进程为单位进行调度运行的话,进程内部又没有办法同时多段代码同时运行,如上面的例子,需要将进程进一步变小,形成多个相对独立的单位线程。一个进程中,可以包含一个或多个线程,但至少有一个线程主线程,就是main函数所在的那个线程,其它的线程都是由主线程直接或间接产生的。这样,一个进程中如果包含了两个线程,那么,在这个进程中,两个线程就可以像上面例子中一样,可相对独立地运行:
4、进程一:主线程:/.线程一:while(true)我听朋友说话;线程二:while(true)我向朋友说话;实际上,由于一台计算机通常只有一个CPU,那么同一个时刻只有一个线程在运行,但是由于计算机运行速度非常地快,可以在10毫秒内运行许多的代码,那么,可以是这样:线程一运行了3毫秒,线程二运行了4毫秒,线程三运行了3毫秒,那么,在10毫秒内,三个线程都得到了运行,我们从外部来看,好像是这三个线程在同时运行。这三个线程之间的切换由JVM完成。9.1.2 线程的状态与生命周期就绪态运行态等待态创建消亡JVM调度调用SleepI/O操作I/O完成线程状态通常有五种基本状态,也就对应着其五个生命周期
5、:1、创建态:当一个线程类被new出来后,这个线程就处于创建态;2、就绪(Ready)态:当线程对象通过start方法调用后,线程就被插入到就绪队列中,处于就绪状态,此时该线程可能还没有开始运行(可能此时有别的线程正在CPU上执行,JVM还没有调试到该线程,那么该线程可能会在就绪队列中呆一段时间,这段时间的长短不确定)。3、运行(Running)态:处于就绪态的线程,万事俱备,只欠CPU,如果JVM在时机成熟时,调度到该线程,那么就会把CPU给该线程,此时,该线程就处于运行态,开始运行。处于运行态的线程,可能由于某种原因(有更重要的线程要运行、自己调用Sleep方法睡觉等),又会重新回到就绪态
6、。4、等待态(Wait):处于运行态的线程,如果进行了IO输入输出调用,如调用了println输出,则JVM会暂停该线程的运行,因为输入输出与CPU无关,且时间要很长。此时JVM会剥夺该线程的CPU,从而该线程会进入到等待态。一旦输入输出完成,则该线程又会进入到就绪态,可以有机会得到CPU再次运行。5、消亡:当该线程运行完成后(通常是在run方法中通过return返回,或者通过exit方法的调用),那么该线程就处于消亡。9.1.3 线程的调试与优先级在就绪态时,线程是在一个就绪队列中(队列可以理解为队伍,可能老长),每个线程是排在队伍的末尾,就是说,可能很多的线程正在就绪队列中,JVM总是从前
7、往后进行调度。所以可以定义线程的优先级,JVM会根据优先级的高低进行优先调度。Java通常定义了10个级别的优先级:Thread.MIN_PRIORITY(值为1)Thread.NORM_PRIORITY(值为5) Thread.MAX_PRIORITY(值为10),值越大,优先级越高(注意,有些操作系统相反,当然在Java中不用管不同的操作系统的区别)可以调用Thread.setPriority方法来设定优先级,通常值就是110,如果如果定义优先级,那么默认取5需要说明的是,即使你把你的线程的优先级定为10(最高),也不能保证一定会先调用你的线程。所以你的编程中,不应该依赖于优先级来保障你的
8、线程一定先运行。所以,编程中,尽量不要使用优先级。在Java中进行多线程编程,主要有三种方法:方法一:创建一个从Thread继承下来的子类,一定要实现其run方法;方法二:创建匿名的Thread类,其中实现了run方法方法三:创建实现Runnable接口的类,然后使用该类来new一个Thread对象。9.2 Thread类的子类创建线程一般方法:class MyThred extends Threadpublic void run()/.在需要的地方,用该类来new一个对象,并调用其start方法来启动该线程:public static void main(String args)MyThre
9、ad t;t=new MyThread();t.start();/启动线程如:public class J_6_7 public static void main(String args) MyThread1 t1;MyThread2 t2;t1=new MyThread1();/构建线程类一对象t2=new MyThread2();/构建线程类二对象t1.start();/启动线程一t2.start();/启动线程二for(int i=1;i=10;i+)/主线程循环System.out.println(主线程);try Thread.sleep(50);/主线程睡眠50毫秒 catch (
10、InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();class MyThread1 extends Threadpublic void run()int i;for(i=1;i=10;i+)System.out.println(这是线程一);try Thread.sleep(50); catch (InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();class MyThread2 extend
11、s Threadpublic void run()int i;for(i=1;i=10;i+)System.out.println(线程二运行中);try Thread.sleep(50); catch (InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();运行的结果可能如下:主线程线程二运行中这是线程一主线程这是线程一线程二运行中主线程这是线程一线程二运行中主线程线程二运行中这是线程一这是线程一主线程线程二运行中这是线程一主线程线程二运行中主线程线程二运行中这是线程一主线程线程二运行中这是线
12、程一主线程线程二运行中这是线程一这是线程一线程二运行中主线程在启动线程时,通过调用该类的start方法,就是运行其run方法。所以线程类中,唯一对外可见的方法就是run方法,其它的方法都是为它服务。9.3 使用匿名Thread类的方法创建线程在Java中,可以使用匿名的方式来创建类,Thread类也可以,但一定要实现run方法,并且要在匿名类后加上.start来启动它,否则的话,无法启动,如:new Thread()public void run() int i;for(i=1;i=10;i+)System.out.println(这是线程三);try Thread.sleep(50); ca
13、tch (InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();.start();运行可能的效果如下:这是线程三主线程这是线程三主线程这是线程三主线程主线程这是线程三主线程这是线程三主线程这是线程三这是线程三主线程主线程这是线程三主线程这是线程三主线程这是线程三9.4 使用Runnable接口的方法创建线程其过程如下:(1)写一个类,实现Runnable的接口,其中至少需要实现其run方法,如:class MyRunnableThread implements Runnable/类的其它的方法、
14、属性public void run()/run方法体(2)在需要的地方定义一个Thread类型的变量:Thread t;(3)new出MyThread类对象MyRunnableThread mrt=new MyRunnableThread ();(4)在需要的地方,new出一个Thread对象,但是,要用到上面的MyThread类对象mrt:t=new Thread(mrt);(5)调用t的start方法,启动线程:t.start();如下例:class MyRunnableThread implements Runnable/别的属性和方法public void run() int i;fo
15、r(i=1;i=10;i+)System.out.println(这是线程四);try Thread.sleep(50); catch (InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();public class J_6_7 public static void main(String args) /*创建方法三:使用Runnable接口*/Thread t;MyRunnableThread mrt=new MyRunnableThread();t=new Thread(mrt);t.sta
16、rt();for(int i=1;i=10;i+)System.out.println(主线程);try Thread.sleep(50); catch (InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();可能的运行效果:主线程这是线程四主线程这是线程四这是线程四主线程这是线程四主线程主线程这是线程四这是线程四主线程这是线程四主线程这是线程四主线程这是线程四主线程主线程这是线程四9.5 线程的常用的方法字段摘要staticintMAX_PRIORITY 线程可以具有的最高优先级。static
17、intMIN_PRIORITY 线程可以具有的最低优先级。staticintNORM_PRIORITY 分配给线程的默认优先级。 构造方法摘要Thread() 分配新的 Thread 对象。Thread(Runnabletarget) 分配新的 Thread 对象。Thread(Runnabletarget, Stringname) 分配新的 Thread 对象。Thread(Stringname) 分配新的 Thread 对象。Thread(ThreadGroupgroup, Runnabletarget) 分配新的 Thread 对象。Thread(ThreadGroupgroup, Ru
18、nnabletarget, Stringname) 分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,并作为 group 所引用的线程组的一员。Thread(ThreadGroupgroup, Runnabletarget, Stringname, longstackSize) 分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,作为 group 所引用的线程组的一员,并具有指定的堆栈大小。Thread(ThreadGroupgroup, Stringname) 分配新的 Thread 对象。 方
19、法摘要staticintactiveCount() 返回当前线程的线程组中活动线程的数目。voidcheckAccess() 判定当前运行的线程是否有权修改该线程。intcountStackFrames() 已过时。该调用的定义依赖于 suspend(),但它遭到了反对。此外,该调用的结果从来都不是意义明确的。staticThreadcurrentThread() 返回对当前正在执行的线程对象的引用。voiddestroy() 已过时。该方法最初用于破坏该线程,但不作任何清除。它所保持的任何监视器都会保持锁定状态。不过,该方法决不会被实现。即使要实现,它也极有可能以 suspend() 方式被
20、死锁。如果目标线程被破坏时保持一个保护关键系统资源的锁,则任何线程在任何时候都无法再次访问该资源。如果另一个线程曾试图锁定该资源,则会出现死锁。这类死锁通常会证明它们自己是“冻结”的进程。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。staticvoiddumpStack() 将当前线程的堆栈跟踪打印至标准错误流。staticintenumerate(Threadtarray) 将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中。staticMapgetAllStackTraces() 返回所有活动线程的
21、堆栈跟踪的一个映射。ClassLoadergetContextClassLoader() 返回该线程的上下文 ClassLoader。staticThread.UncaughtExceptionHandlergetDefaultUncaughtExceptionHandler() 返回线程由于未捕获到异常而突然终止时调用的默认处理程序。longgetId() 返回该线程的标识符。StringgetName() 返回该线程的名称。intgetPriority() 返回线程的优先级。StackTraceElementgetStackTrace() 返回一个表示该线程堆栈转储的堆栈跟踪元素数组。Th
22、read.StategetState() 返回该线程的状态。ThreadGroupgetThreadGroup() 返回该线程所属的线程组。Thread.UncaughtExceptionHandlergetUncaughtExceptionHandler() 返回该线程由于未捕获到异常而突然终止时调用的处理程序。staticbooleanholdsLock(Objectobj) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。voidinterrupt() 中断线程。staticbooleaninterrupted() 测试当前线程是否已经中断。booleanisAlive(
23、) 测试线程是否处于活动状态。booleanisDaemon() 测试该线程是否为守护线程。booleanisInterrupted() 测试线程是否已经中断。voidjoin() 等待该线程终止。voidjoin(longmillis) 等待该线程终止的时间最长为 millis 毫秒。voidjoin(longmillis, intnanos) 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。voidresume() 已过时。该方法只与 suspend() 一起使用,但 suspend() 已经遭到反对,因为它具有死锁倾向。有关更多信息,请参阅为何不赞成使用 Threa
24、d.stop、Thread.suspend 和 Thread.resume?。voidrun() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。voidsetContextClassLoader(ClassLoadercl) 设置该线程的上下文 ClassLoader。voidsetDaemon(booleanon) 将该线程标记为守护线程或用户线程。staticvoidsetDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandle
25、reh) 设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。voidsetName(Stringname) 改变线程名称,使之与参数 name 相同。voidsetPriority(intnewPriority) 更改线程的优先级。voidsetUncaughtExceptionHandler(Thread.UncaughtExceptionHandlereh) 设置该线程由于未捕获到异常而突然终止时调用的处理程序。staticvoidsleep(longmillis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度
26、程序精度和准确性的影响。staticvoidsleep(longmillis, intnanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。voidstart() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。voidstop() 已过时。该方法具有固有的不安全性。用 Thread.stop 来终止线程将释放它已经锁定的所有监视器(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其他线程可见,这有可能导
27、致任意的行为。stop 的许多使用都应由只修改某些变量以指示目标线程应该停止运行的代码来取代。目标线程应定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。如果目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt 方法来中断该等待。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。voidstop(Throwableobj) 已过时。该方法具有固有的不安全性。有关详细信息,请参阅 stop()。该方法的附加危险是它可用于生成目标线程未准备处理的异常(包括若没有该方法该线程不太可能
28、抛出的已检查的异常)。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。voidsuspend() 已过时。该方法已经遭到反对,因为它具有固有的死锁倾向。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。如果重新开始目标线程的线程想在调用 resume 之前锁定该监视器,则会发生死锁。这类死锁通常会证明自己是“冻结”的进程。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。StringtoSt
29、ring() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。staticvoidyield() 暂停当前正在执行的线程对象,并执行其他线程。1、start方法:在需要启动的地方进行调用,实际上就是运行该类中的run方法,如果一个线程类变量没有被new出来,就直接调用start方法,会出错,如:Thread t,w;MyRunnableThread mrt=new MyRunnableThread();t=new Thread(mrt);t=null;t.start();/可通过编译,但运行时会抛出异常w.start();/更通不过编译其中的t虽然被new出来了,但是后来又释放了(通
30、过t=null),那么t已经不存在了,不得调用其start方法.2、run方法:run方法没有参数,该方法不得直接调用,只能通过.start调用。如:还有几个小问题:(1)run方法结束的问题:run方法应该让其自然结束(自己死亡),最好的办法就是通过return返回。但有时我们会让run方法执行一个死循环,如:class MyThread1 extends Threadpublic void run()int i;while(true)System.out.println(这是线程一);try Thread.sleep(50); catch (InterruptedException e)
31、/ TODO Auto-generated catch blocke.printStackTrace();则该线程将永远运行下去,我们应该想办法让其自己结束,方法有:方法一:不要让其while(true)循环,而是应该让其while(flag),其中的flag为boolean类型,可以初始值为true,然后在程序运行当中改变其为false,那么run就会自己结束。如class MyThread1 extends Threadboolean flag;public MyThread1()flag=true;public void run()int i;while(flag)System.out.
32、println(这是线程一);try Thread.sleep(50); catch (InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();方法二,仍然使用一个判断,当符合条件时,让其直接return:class MyThread1 extends Threadboolean flag;public MyThread1()flag=true;public void run()int i;while(flag)System.out.println(这是线程一);if(满足什么条件)return;
33、try Thread.sleep(50); catch (InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();(2)如果一个线程已经被start了,它如果还处在运行或sleep中,不应该再调用它,就是不应该第二次start,否则,结果会难以预料,如:Thread t,w;MyRunnableThread mrt=new MyRunnableThread();t=new Thread(mrt);t.start();for(int i=1;i=10;i+)System.out.println(主线
34、程);try Thread.sleep(50); catch (InterruptedException e) / TODO Auto-generated catch blocke.printStackTrace();t.start();同一个t被两次start,则运行时会出现异常:主线程这是线程四这是线程四主线程主线程这是线程四这是线程四主线程主线程这是线程四主线程这是线程四主线程这是线程四这是线程四主线程主线程这是线程四这是线程四主线程Exception in thread main java.lang.IllegalThreadStateExceptionat java.lang.Thread.start(Unknown Source)at J_6_7.main(J_6_7.java:46)3、静态sleep方法:static void sl
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 软骨炎常见症状及护理培训
- 高压电缆耐压试验施工方案
- 防火涂料施工方案
- 2025 六年级生物学下册荒漠生态系统的食物链简单性分析课件
- 医药生物行业2026年度创新药新技术全面突破战略看多中国创新出海
- 睡莲人工杂交授粉技术规程(征求意见)
- 2026年短视频脚本创作方法培训
- 2026年新疆石河子职业技术学院单招(计算机)测试备考题库及答案参考
- 2026中级会计三科高频题库100道含完整答案(典优)
- 2026年常州工学院辅导员招聘备考题库及1套参考答案
- 水务公司2026年节后复工安全生产培训
- (2025年)泰兴市事业单位招聘财务会计知识试题及答案
- 2026内蒙古地质矿产集团有限公司社会招聘65人备考题库带答案详解(b卷)
- 《力与大地:重力、摩擦力的科学透视与地理联结》-初中科学(八年级)单元复习课教学设计
- 2025年宁波职业技术学院单招职业技能考试题库附答案解析
- 工程地质工程施工钻探工春节后复工安全考核试卷含答案
- 2025年曼迪匹艾笔试真题及答案
- 江苏省13市2026届高一上数学期末经典试题含解析
- 2026年山东单招职业适应性测试时政经典题集含答案
- 2026年内蒙古单招新能源汽车技术专业技能故障诊断经典题集含答案
- 2025锅炉使用单位锅炉使用安全风险日管控、周排查、月调度管理制度
评论
0/150
提交评论