浅谈Java线程的生命周期

开发 后端
本文将谈到的是Java线程的生命周期,Java线程可以通过直接实例化Thread对象或实例化继承Thread的对象来创建其它线程。

创建Java线程

在Java程序中创建线程有几种方法。每个Java程序至少包含一个线程:主线程。其它线程都是通过Thread构造器或实例化继承类Thread的类来创建的。

51CTO编辑推荐:Java线程从入门到实践

Java线程可以通过直接实例化Thread对象或实例化继承Thread的对象来创建其它线程。在线程基础中的示例(其中,我们在十秒钟之内计算尽量多的素数)中,我们通过实例化CalculatePrimes类型的对象(它继承了Thread),创建了一个线程。

当我们讨论Java程序中的线程时,也许会提到两个相关实体:完成工作的实际线程或代表线程的Thread对象。正在运行的线程通常是由操作系统创建的;Thread对象是由JavaVM创建的,作为控制相关线程的一种方式。

创建线程和启动线程并不相同

在一个线程对新线程的Thread对象调用start()方法之前,这个新线程并没有真正开始执行。Thread对象在其线程真正启动之前就已经存在了,而且其线程退出之后仍然存在。这可以让您控制或获取关于已创建的线程的信息,即使线程还没有启动或已经完成了。

通常在构造器中通过start()启动线程并不是好主意。这样做,会把部分构造的对象暴露给新的线程。如果对象拥有一个线程,那么它应该提供一个启动该线程的start()或init()方法,而不是从构造器中启动它。(请参阅参考资料,获取提供此概念更详细说明的文章链接。)

结束Java线程

Java线程会以以下三种方式之一结束:

Java线程到达其run()方法的末尾。

Java线程抛出一个未捕获到的Exception或Error。

另一个Java线程调用一个弃用的stop()方法。弃用是指这些方法仍然存在,但是您不应该在新代码中使用它们,并且应该尽量从现有代码中除去它们。

当Java程序中的所有线程都完成时,程序就退出了。

加入Java线程

ThreadAPI包含了等待另一个线程完成的方法:join()方法。当调用Thread.join()时,调用线程将阻塞,直到目标线程完成为止。

Thread.join()通常由使用线程的程序使用,以将大问题划分成许多小问题,每个小问题分配一个线程。本章结尾处的示例创建了十个线程,启动它们,然后使用Thread.join()等待它们全部完成。

Java线程调度

除了何时使用Thread.join()和Object.wait()外,线程调度和执行的计时是不确定的。如果两个线程同时运行,而且都不等待,您必须假设在任何两个指令之间,其它线程都可以运行并修改程序变量。如果线程要访问其它线程可以看见的变量,如从静态字段(全局变量)直接或间接引用的数据,则必须使用同步以确保数据一致性。

在以下的简单示例中,我们将创建并启动两个线程,每个线程都打印两行到System.out:

  1. publicclassTwoThreads{  
  2. publicstaticclassThread1extendsThread{  
  3. publicvoidrun(){  
  4. System.out.println("A");  
  5. System.out.println("B");  
  6. }  
  7. }  
  8. publicstaticclassThread2extendsThread{  
  9. publicvoidrun(){  
  10. System.out.println("1");  
  11. System.out.println("2");  
  12. }  
  13. }  
  14. publicstaticvoidmain(String[]args){  
  15. newThread1().start();  
  16. newThread2().start();  
  17. }  

我们并不知道这些行按什么顺序执行,只知道“1”在“2”之前打印,以及“A”在“B”之前打印。输出可能是以下结果中的任何一种:

12AB

1A2B

1AB2

A12B

A1B2

AB12

不仅不同机器之间的结果可能不同,而且在同一机器上多次运行同一程序也可能生成不同结果。永远不要假设一个线程会在另一个线程之前执行某些操作,除非您已经使用了同步以强制一个特定的执行顺序。

休眠

ThreadAPI包含了一个sleep()方法,它将使当前线程进入等待状态,直到过了一段指定时间,或者直到另一个线程对当前线程的Thread对象调用了Thread.interrupt(),从而中断了线程。当过了指定时间后,线程又将变成可运行的,并且回到调度程序的可运行线程队列中。

如果线程是由对Thread.interrupt()的调用而中断的,那么休眠的线程会抛出InterruptedException,这样线程就知道它是由中断唤醒的,就不必查看计时器是否过期。

Thread.yield()方法就象Thread.sleep()一样,但它并不引起休眠,而只是暂停当前线程片刻,这样其它线程就可以运行了。在大多数实现中,当较高优先级的线程调用Thread.yield()时,较低优先级的线程就不会运行。

CalculatePrimes示例使用了一个后台线程计算素数,然后休眠十秒钟。当计时器过期后,它就会设置一个标志,表示已经过了十秒。

守护程序线程

我们提到过当Java程序的所有线程都完成时,该程序就退出,但这并不完全正确。隐藏的系统线程,如垃圾收集线程和由JVM创建的其它线程会怎么样?我们没有办法停止这些线程。如果那些线程正在运行,那么Java程序怎么退出呢?

这些系统线程称作守护程序线程。Java程序实际上是在它的所有非守护程序线程完成后退出的。

任何线程都可以变成守护程序线程。可以通过调用Thread.setDaemon()方法来指明某个线程是守护程序线程。您也许想要使用守护程序线程作为在程序中创建的后台线程,如计时器线程或其它延迟的事件线程,只有当其它非守护程序线程正在运行时,这些线程才有用。

示例:用多个Java线程分解大任务

在这个示例中,TenThreads显示了一个创建了十个线程的程序,每个线程都执行一部分工作。该程序等待所有线程全部完成,然后收集结果。

  1. /**  
  2. *Createstenthreadstosearchforthemaximumvalueofalargematrix.  
  3. *Eachthreadsearchesoneportionofthematrix.  
  4. */  
  5. publicclassTenThreads{  
  6. privatestaticclassWorkerThreadextendsThread{  
  7. intmax=Integer.MIN_VALUE;  
  8. int[]ourArray;  
  9. publicWorkerThread(int[]ourArray){  
  10. this.ourArray=ourArray;  
  11. }  
  12. //Findthemaximumvalueinourparticularpieceofthearray  
  13. publicvoidrun(){  
  14. for(inti=0;i<ourArray.length;i++)  
  15. max=Math.max(max,ourArray[i]);  
  16. }  
  17. publicintgetMax(){  
  18. returnmax;  
  19. }  
  20. }  
  21. publicstaticvoidmain(String[]args){  
  22. WorkerThread[]threads=newWorkerThread[10];  
  23. int[][]bigMatrix=getBigHairyMatrix();  
  24. intmax=Integer.MIN_VALUE;  
  25. //Giveeachthreadasliceofthematrixtoworkwith  
  26. for(inti=0;i<10;i++){  
  27. threads[i]=newWorkerThread(bigMatrix[i]);  
  28. threads[i].start();  
  29. }  
  30. //Waitforeachthreadtofinish  
  31. try{  
  32. for(inti=0;i<10;i++){  
  33. threads[i].join();  
  34. max=Math.max(max,threads[i].getMax());  
  35. }  
  36. }  
  37. catch(InterruptedExceptione){  
  38. //fallthrough  
  39. }  
  40. System.out.println("Maximumvaluewas"+max);  
  41. }  

Java线程小结

就象程序一样,线程有生命周期:它们启动、执行,然后完成。一个程序或进程也许包含多个线程,而这些线程看来互相单独地执行。

线程是通过实例化Thread对象或实例化继承Thread的对象来创建的,但在对新的Thread对象调用start()方法之前,这个线程并没有开始执行。当线程运行到其run()方法的末尾或抛出未经处理的异常时,它们就结束了。

sleep()方法可以用于等待一段特定时间;而join()方法可能用于等到另一个线程完成。

【编辑推荐】

  1. JavaScript中关于 Cookie的详细介绍
  2. JavaScript中 confirm,alert,prompt的用法
  3. 基于JavaScript的REST 客户端框架
  4. 如何优化JavaScript脚 本的性能
  5. 用Javascript连接 Access数据库的方法
责任编辑:彭凡 来源: cnblogs
相关推荐

2009-06-29 18:03:15

Java多线程线程的生命周期

2010-07-14 10:48:37

Perl线程

2023-10-26 08:25:35

Java线程周期

2012-01-16 09:00:56

线程

2010-07-14 10:59:15

Perl线程

2009-07-31 17:53:39

ASP.NET线程安全

2009-08-04 16:05:15

ASP.NET页面生命

2012-05-28 15:37:20

WP程序生命周期

2015-07-08 16:28:23

weak生命周期

2012-04-28 13:23:12

Java生命周期

2022-04-19 07:20:24

软件开发安全生命周期SSDLC应用安全

2009-06-23 18:11:02

JSF的生命周期Ajax处理

2009-06-11 11:28:35

JSF生命周期

2020-03-30 13:20:58

线程Java编程语言

2009-05-21 09:12:41

Java开发平台生命周期管理

2011-06-16 09:31:21

ActivityAndroid

2009-06-17 15:06:50

Hibernate实体

2019-10-16 10:50:13

Linux内核测试

2013-07-29 05:11:38

iOS开发iOS开发学习类的'生命周期'

2020-09-08 15:14:51

线程 APIs周期
点赞
收藏

51CTO技术栈公众号