JavaBean】线程生命周期是什么?

2020-08-19 09:58发布

4条回答
偷猫的鱼
2楼 · 2020-08-19 09:59

线程从创建到结束的一个过程就是线程的生命周期。在线程的生命周期中,有不同的状态来描述。

状态的罗列:

新建态:线程刚刚创建之后的状态

就绪态:准备好了各种资源,等待cpu来临

运行态:正在运行的状态

阻塞态:线程休眠,IO,等到锁对象

死亡态:线程正常执行结束,碰到异常结束程序,调用方法结束线程


Dong
3楼 · 2020-08-19 10:13

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

(1)生命周期的五种状态

新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread t1=new Thread();

就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();

运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。

死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。

自然终止:正常运行run()方法后终止

异常终止:调用stop()方法让一个线程终止运行

堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。

正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。

正在等待:调用wait()方法。(调用motify()方法回到就绪状态)

被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)


主要有 new , ready , running , waiting , terminated 5 种状态

其中:

  • new 只是说,这个线程被创建了,但是还不允许分配 CPU 执行。因为这个状态只是说明你在编程语言层面被创建了,操作系统层面还没有被创建,肯定就谈不上分配 CPU 执行了

  • ready 这个状态是说,在操作系统层面已经成功创建了,所以接下来就是等待分配 CPU 执行了。还记得那句经典的嘛?ready ?go !

  • running 的状态,相信你就知道了,我都已经 ready 了,此时如果再给我分配一下 CPU 我是不是就可以 go 了?那不就是 running 状态了嘛

  • waiting 状态,就是线程在 running 状态的时候,突然发现,哎,我需要进行一下 I/O 操作,或者需要等待某个事件发生(比如说需要某个条件变量),这个时候是不是就不能再继续 happy 的 running 了。那咋办?waiting 一下呗

    • 那你都 waiting 了,占用的 CPU 资源是不是应该释放掉?所以说, waiting 状态的线程是永远没有机会获得 CPU 使用权的

    • 你是不是一听「永远没有机会」这几个字就给吓坏了,我该不会永远没有机会执行了吧。放心吧,你不是在 waiting 嘛,等你 wait 的事件发生了,就可以继续到 running 状态

  • 当整个线程执行完毕,或者出现异常的时候,就进入了 terminated 状态,也就是线程的使命就完成啦,处于 terminated 状态的线程不会再切换到其他状态了


我是大脸猫
5楼 · 2020-08-20 09:41

线程(thread, 台湾称 执行绪)是"进程"中某个单一顺序的控制流。也被称为轻量进程(lightweight processes)。计算机科学术语,指运行中的程序的调度单位。

      线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。线程可以创建和撤消线程,从而实现程序的并发执行。一般,线程具有就绪、阻塞和运行三种基本状态。

      在多中央处理器的系统里,不同线程可以同时在不同的中央处理器上运行,甚至当它们属于同一个进程时也是如此。大多数支持多处理器的操作系统都提供编程接口来让进程可以控制自己的线程与各处理器之间的关联度(affinity)。

      有时候,线程也称作轻量级进程。就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其它每个进程应有的状态。

      进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。尽管这让线程之间共享信息变得更容易,但您必须小心,确保它们不会妨碍同一进程里的其它线程。

      Java 线程工具和 API 看似简单。但是,编写有效使用线程的复杂程序并不十分容易。因为有多个线程共存在相同的内存空间中并共享相同的变量,所以您必须小心,确保您的线程不会互相干扰。

一、线程的概念 

  一般来说,我们把正在计算机中执行的程序叫做"进程"(Process) ,而不将其称为程序(Program)。所谓"线程"(Thread),是"进程"中某个单一顺序的控制流。
 
新兴的操作系统,如Mac,Windows NT,Windows 95等,大多采用多线程的概念,把线程视为基本执行单位。线程也是Java中的相当重要的组成部分之一。 

  甚至最简单的Applet也是由多个线程来完成的。在Java中,任何一个Applet的paint()和update()方法都是由AWT(Abstract Window Toolkit)绘图与事件处理线程调用的,而Applet 主要的里程碑方法——init(),start(),stop()和destory()  ——是由执行该Applet的应用调用的。 

  单线程的概念没有什么新的地方,真正有趣的是在一个程序中同时使用多个线程来完成不同的任务。某些地方用轻量进程(Lightweig ht Process)来代替线程,线程与真正进程的相似性在于它们都是单一顺序控制流。然而线程被认为轻量是由于它运行于整个程序的上下文内,能使用整个程序共有的资源和程序环境。 

  作为单一顺序控制流,在运行的程序内线程必须拥有一些资源作为必要的开销。例如,必须有执行堆栈和程序计数器。在线程内执行的代码只在它的上下文中起作用,因此某些地方用"执行上下文"来代替"线程"。 

二、线程属性 

  为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统。必须知道如何提供线程体、线程的生命周期、实时系统如 何调度线程、线程组、什么是幽灵线程(Demo nThread)。 

  (1)线程体 
  所有的操作都发生在线程体中,在Java中线程体是从Thread类继承的run()方法,或实现Runnable接口的类中的run()方法。当线程产生并初始化后,实时系统调用它的run()方法。run()方法内的代码实现所产生线程的行为,它是线程的主要部分。 

  (2)线程状态 
  附图表示了线程在它的生命周期内的任何时刻所能处的状态以及引起状态改变的方法。这图并不是完整的有限状态图,但基本概括了线程中比较感兴趣和普遍的方面。以下讨论有关线程生命周期以此为据。 


  ●新线程态(New Thread) 
  产生一个Thread对象就生成一个新线程。当线程处于"新线程"状态时,仅仅是一个空线程对象,它还没有分配到系统资源。因此只能启动或终止它。任何其他操作都会引发异常。
 
  ●可运行态(Runnable) 
  start()方法产生运行线程所必须的资源,调度线程执行,并且调用线程的run()方法。在这时线程处于可运行态。该状态不称为运行态是因为这时的线程并不总是一直占用处理机。特别是对于只有一个处理机的PC而言,任何时刻只能有一个处于可运行态的线程占用处理 机。Java通过调度来实现多线程对处理机的共享。 

  ●非运行态(Not Runnable) 
  当以下事件发生时,线程进入非运行态。 
  ①suspend()方法被调用; 
  ②sleep()方法被调用; 
  ③线程使用wait()来等待条件变量; 
  ④线程处于I/O等待。 

  ●死亡态(Dead) 
  当run()方法返回,或别的线程调用stop()方法,线程进入死亡态 。通常Applet使用它的stop()方法来终止它产生的所有线程。 

  (3)线程优先级 
  虽然我们说线程是并发运行的。然而事实常常并非如此。正如前面谈到的,当系统中只有一个CPU时,以某种顺序在单CPU情况下执行多线程被称为调度(scheduling)。Java采用的是一种简单、固定的调度法,即固定优先级调度。这种算法是根据处于可运行态线程的相对优先级来实行调度。当线程产生时,它继承原线程的优先级。在需要时可对优先级进行修改。在任何时刻,如果有多条线程等待运行,系统选择优先级最高的可运行线程运行。只有当它停止、自动放弃、或由于某种原因成为非运行态低优先级的线程才能运行。如果两个线程具有相同的优先级,它们将被交替地运行。 

  Java实时系统的线程调度算法还是强制性的,在任何时刻,如果一个比其他线程优先级都高的线程的状态变为可运行态,实时系统将选择该线程来运行。 

  (4)幽灵线程 
  任何一个Java线程都能成为幽灵线程。它是作为运行于同一个进程内的对象和线程的服务提供者。例如,HotJava浏览器有一个称为" 后台图片阅读器"的幽灵线程,它为需要图片的对象和线程从文件系统或网络读入图片。 

  幽灵线程是应用中典型的独立线程。它为同一应用中的其他对象和线程提供服务。幽灵线程的run()方法一般都是无限循环,等待服务请求。 

  (5)线程组 
  每个Java线程都是某个线程组的成员。线程组提供一种机制,使得多个线程集于一个对象内,能对它们实行整体操作。譬如,你能用一个方法调用来启动或挂起组内的所有线程。Java线程组由ThreadGroup类实现。
 
  当线程产生时,可以指定线程组或由实时系统将其放入某个缺省的线程组内。线程只能属于一个线程组,并且当线程产生后不能改变它所属的线程组。 

三、多线程程序 

  对于多线程的好处这就不多说了。但是,它同样也带来了某些新的麻烦。只要在设计程序时特别小心留意,克服这些麻烦并不算太困难。 

  (1)同步线程 
  许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态。这就需要同步机制。在Java中每个对象都有一把锁与之对应。但Java不提供单独的lock和unlock操作。它由高层的结构隐式实现, 来保证操作的对应。(然而,我们注意到Java虚拟机提供单独的monito renter和monitorexit指令来实现lock和unlo 
ck操作。) 

      synchronized语句计算一个对象引用,试图对该对象完成锁操作, 并且在完成锁操作前停止处理。当锁操作完成synchronized语句体得到执行。当语句体执行完毕(无论正常或异常),解锁操作自动完成。作为面向对象的语言,synchronized经常与方法连用。一种比较好的办法是,如果某个变量由一个线程赋值并由别的线程引用或赋值,那么所有对该变量的访问都必须在某个synchromized语句或synchronized方法内。 

  现在假设一种情况:线程1与线程2都要访问某个数据区,并且要求线程1的访问先于线程2, 则这时仅用synchronized是不能解决问题的。这在Unix或Windows NT中可用Simaphore来实现。而Java并不提供。在Java中提供的是wait()和notify()机制。使用如下: 

  synchronized method-1(…){ call by thread 1. 
  ∥access data area; 
  available=true; 
  notify() 
  } 
  synchronized method-2(…){∥call by thread 2. 
  while(!available) 
  try{ 
  wait();∥wait for notify(). 
  }catch (Interrupted Exception e){ 
  } 
  ∥access data area 
  } 
  其中available是类成员变量,置初值为false。 

  如果在method-2中检查available为假,则调用wait()。wait()的作用是使线程2进入非运行态,并且解锁。在这种情况下,method-1可以被线程1调用。当执行notify()后。线程2由非运行态转变为可运行态。当method-1调用返回后。线程2可重新对该对象加锁,加锁成功后执行wait()返回后的指令。这种机制也能适用于其他更复杂的情况。 

  (2)死锁 
  如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源。系统中没有饿死和死锁的线程。Java并不提供对死锁的检测机制。对大多数的Java程序员来说防止死锁是一种较好的选择。最简单的防止死锁的方法是对竞争的资源引入序号,如果一个线程需要几个资源,那么它必须先得到小序号的资源,再申请大序号的资源。 

四、线程和进程的比较


   进程是资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块PCB中。以表示该进程拥有这些资源或正在使用它们。
    另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。

    与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。
   当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。

   线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成。寄存器可被用来存储线程内的局部变量,但不能存储其他线程的相关变量。

   发生进程切换与发生线程切换时相比较,进程切换时涉及到有关资源指针的保存以及地址空间的变化等问题;线程切换时,由于同不进程内的线程共享资源和地址 空间,将不涉及资源信息的保存和地址变化问题,从而减少了操作系统的开销时间。而且,进程的调度与切换都是由操作系统内核完成,而线程则既可由操作系统内 核完成,也可由用户程序进行。

      图1 多线程与进程之间的关系

五、线程的适用范围

    典型的应用

 1.服务器中的文件管理或通信控制

 2.前后台处理

 3.异步处理


六、线程的执行特性

      一个线程必须处于如下四种可能的状态之一:

      初始态:一个线程调用了new方法之后,并在调用start方法之前的所处状态。在初始态中,可以调用start和stop方法。

      Runnable:一旦线程调用了start 方法,线程就转到Runnable 状态,注意,如果线程处于Runnable状态,它也有可能不在运行,这是因为还有优先级和调度问题。

      阻塞/ NonRunnable:线程处于阻塞/NonRunnable状态,这是由两种可能性造成的:要么是因挂起而暂停的,要么是由于某些原因而阻塞的,例如包括等待IO请求的完成。 退出:线程转到退出状态,这有两种可能性,要么是run方法执行结束,要么是调用了stop方法。

      最后一个概念就是线程的优先级,线程可以设定优先级,高优先级的线程可以安排在低优先级线程之前完成。一个应用程序可以通过使用线程中的方法setPriority(int),来设置线程的优先级大小。

      线程有5种基本操作:

 派生:线程在进程内派生出来,它即可由进程派生,也可由线程派生。
 阻塞(Block):如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。
 激活(unblock):如果阻塞线程的事件发生,则该线程被激活并进入就绪队列。
 调度(schedule):选择一个就绪线程进入执行状态。
 结束(Finish):如果一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。


      图2 线程的状态与操作
      线程的另一个执行特性是同步。线程中所使用的同步控制机制与进程中所使用的同步控制机制相同。

七、线程的分类

      线程有两个基本类型:

     用户级线程:管理过程全部由用户程序完成,操作系统内核心只对进程进行管理。

     系统级线程(核心级线程):由操作系统内核进行管理。操作系统内核给应用程序提供相应的系统调用和应用程序接口API,以使用户程序可以创建、执行、撤消线程。

附:线程举例

      1. SUN Solaris 2.3

      Solaris支持内核线程、轻权进程和用户线程。一个进程可有大量用户线程;大量用户线程复用少量的轻权进程,轻权进程与内核线程一一对应。
      用户级线程在调用核心服务时(如文件读写),需要“捆绑(bound)”在一个LWP上。永久捆绑(一个LWP固定被一个用户级线程占用,该LWP移到LWP池之外)和临时捆绑(从LWP池中临时分配一个未被占用的LWP)。
      在调用系统服务时,如果所有LWP已被其他用户级线程所占用(捆绑),则该线程阻塞直到有可用的LWP。
      如果LWP执行系统线程时阻塞(如read()调用),则当前捆绑在LWP上的用户级线程也阻塞。
                                   

      图3  用户线程、轻权进程和核心线程的关系

 ¨        有关的C库函数
        /* 创建用户级线程             */
    int thr_create(void *stack_base, size_t stack_size,
    void *(*start_routine)(void *), void *arg, long flags,
    thread_t *new_thread_id); 
  其中flags包括:THR_BOUND(永久捆绑), THR_NEW_LWP(创建新LWP放入LWP池),若两者同时指定则创建两个新LWP,一个永久捆绑而另一个放入LWP池。
 ²        有关的系统调用
   /* 在当前进程中创建LWP     */
  int _lwp_create(ucontext_t *contextp, unsigned long flags,

  lwpid_t *new_lwp_id);
  /* 构造LWP上下文          */
  void _lwp_makecontext(ucontext_t *ucp,
   void (*start_routine)( void *), void *arg,
  void *private, caddr_t stack_base, size_t stack_size);
  /* 注意:没有进行“捆绑”操作的系统调用 */

    2. Windows NT
      NT线程的上下文包括:寄存器、核心栈、线程环境块和用户栈。
      NT线程状态
   (1) 就绪状态:进程已获得除处理机外的所需资源,等待执行。
   (2) 备用状态:特定处理器的执行对象,系统中每个处理器上只能有一个处于备用状态的线程。
   (3) 运行状态:完成描述表切换,线程进入运行状态,直到内核抢先、时间片用完、线程终止或进行等待状态。
   (4) 等待状态:线程等待对象句柄,以同步它的执行。等待结束时,根据优先级进入运行、就绪状态。
   (5) 转换状态:线程在准备执行而其内核堆栈处于外存时,线程进入转换状态;当其内核堆栈调回内存,线程进入就绪状态。
   (6) 终止状态:线程执行完就进入终止状态;如执行体有一指向线程对象的指针,可将线程对象重新初始化,并再次使用。


                   

      图4  Windows NT的线程状态
  
  
      NT线程的有关API
   
      CreateThread()函数在调用进程的地址空间上创建一个线程,以执行指定的函数;返回值为所创建线程的句柄。
      ExitThread()函数用于结束本线程。
      SuspendThread()函数用于挂起指定的线程。
      ResumeThread()函数递减指定线程的挂起计数,挂起计数为0时,线程恢复执行。

https://blog.csdn.net/dui123/article/details/1768899?ops_request_misc={"request_id":"159788767519724835815666","scm":"20140713.130102334.."}&request_id=159788767519724835815666&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-1-1768899.first_rank_ecpm_v3_pc_rank_v2&utm_term=线程生命周期是什么&spm=1018.2118.3001.4187

相关问题推荐

  • 回答 9

    Java的线程生命周期有六种状态:New(初始化状态)Runnable(就绪状态)Running(运行状态)Blocked(阻塞状态)Terminated(终止状态)

  • 回答 3

    如果使用常量的方式,该对象将被存储在常量池(永久代)如果使用new的方式,该对象将被存储在堆

  • 回答 7

    同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。...

  • 回答 6

    通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的

  • 回答 16

            Vue.js是一款流行的JavaScript前端框架,一个用于创建用户界面的开源JavaScript框架,旨在更好地组织与简化 Web开发。        Vue所关注的核心是MVC模式中的视图层,同时,它也能方便地获取数据更新,并通过组件内部特定的方法实现...

  • 回答 9

    1、throw和throws两个都是Java语言中的关键字2、throw关键字是用来抛出异常对象,throws关键字是用来声明声明异常的类型3、throw只能抛出一个异常对象,throws可以抛出多个异常类型

  • 回答 4

    MVC(Model-View-Controller,模型—视图—控制器模式)用于表示一种软件架构模式。它把软件系统分为三个基本部分:模型(Model),视图(View)和控制器(Controller)。

  • 回答 1

    都是为了完成数据的封装可以理解为一种数据结构

  • 回答 2

    就业老师会给讲怎样撰写简历,培训面试技巧,技术老师也会给梳理项目,整的明明白白的,然后进行多轮的模拟面试,后面就业老师也会一直跟踪班级学员情况,还会给推荐一些岗位,...

  • 回答 4
    已采纳

    由于我们是是刚开始学习java知识,对相应的知识点还不是太了解,这个时候我们需要制定一个详细的计划,根据自己能进行学习的时间和自己的学习能力实际情况进行确定,不要好高骛远,第一天要学习20个小时,然后接下来几天都觉得第一天学习太累,再休息几天,这...

没有解决我的问题,去提问