• [Thread,Thread State]线程,线程状态


    线程

    线程 & 进程

    学过操作系统的基本都知道这个词。一开始出现的是进程,进程是操作系统中分时系统的一个基本运作单位。但是后来由于系统设计的升级,对进程又进行了划分,所以出现了线程。
    进程可以包含多个线程,至少一个。这样就出现了线程来执行用户需要执行的命令。
    区别总结一下:

    1. 进程是一段正在执行的程序,是资源分配的基本单元,而线程时CPU调度的基本单元。
    2. 进程间相互独立,进程与进程之间不能共享资源,一个进程至少有一个线程,同一个进程的各线程共享整个进程的资源(寄存器,堆栈,上下文)。
    3. 线程的创建和切换开销比进程小。

    JAVA中的线程

    高级语言都有一套使用线程的方法。在JAVA中最原始的方法就2种:

    1. 继承Thread
    2. 实现Runnable接口

    当然除了这里的最原始的方法,还有一些方法可以创建线程。

    线程的状态

    线程状态大致可以分为5种:

    1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
    2. 可运行(RUNNABLE):也称为就绪状态,调用start方法后线程就计入就绪状态但并不是说只要调用start方法线程就马上变为当前线程,在变为当前线程之前都是就绪状态。线程在睡眠和挂起中恢复的时候也会进入就绪状态。线程对象创建后,其他的线程调用了它的start方法,这个线程就会进入可运行线程池中,等待被调度选中,获取CPU的使用权。
    3. 运行(RUNNING):可运行状态(RUNNABLE)的线程获得了CPU的时间片,执行程序代码,开始执行run
    4. 阻塞(BLOCKED):线程由于某种原因放弃了CPU的使用权,也就是时间片,暂时停止了运行。
      a. 等待阻塞: 通过调用线程的wait方法,让线程等待某工作的完成,线程进入等待队列(waitting queue)。
      b. 同步阻塞: 线程在获取synchronized失败,会进入同步阻塞状态,线程进入阻塞。
      c. 其他阻塞: 通过调用线程的sleep或者join或发出了I/O请求,线程会进入阻塞。当sleep超时,join等待线程终止或超时,或I/O完毕,线程就重新进入可运行状态(RUNNABLE)
    5. 死亡(DEAD):线程执行结束,run,main方法结束,或者因为异常退出了run,该线程结束生命周期,状态不可逆转。

    当然在JDK中,线程的状态有NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED,和系统层面的划分差不多。

    1. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
    2. 限时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
    3. 终止(TERMINATED):表示该线程已经执行完毕。

    线程状态图

    初始状态:

    实现了Runnable接口或者继承了Thread的一个类,通过new创建了实例,线程就进入了初始状态。

    可运行状态:

    • 可运行状态只是代表该线程在随时等待调度程序。
    • 调用线程的Start方法,线程进入可运行状态。
    • 当前线程sleep结束,其他线程join结束,IO结束,拿到了对象锁,线程也可以进入可运行状态。
    • 当前线程时间片用完后,调用了该线程的yield方法,该线程进入可运行状态。

    运行状态:

    进入运行状态RUNNING,只有等到被调度程序选中,才能够进入。

    死亡状态:

    • 当线程run完成时,或者main方法完成,就认为dead。
    • 如果尝试在一个dead的线程上start,会抛出IllegalThreadStateException

    阻塞状态:

    • 当前线程调用了sleep方法,当前线程进入阻塞。
    • 运行在当前线程里的其他线程调用了join方法,当前线程进入阻塞
    • 等待IO时,当前线程进入阻塞。

    有很多的博客,提到了一些锁池,等待队列,等待池,可运行池的概念,但是我一直没有找到这些名词的来源,没有哪本书中,提过这些名词。但是通过这些名词,解释了一些可能不容易理解的问题。
    从一个国外站上面看到一个文章说是这样解释的:
    Inside the Java Virtual Machine

    在JAVA中,每个对象都有一个内部锁(Monitor)。JVM会为每个对象维护2个区域,Entry Set(入口集),Wait Set(等待集)。Entry Set存储了等待获取object的内部锁的所有线程,Wait Set存储了执行了object.wait方法的线程。

    如果有个线程A执行了wait,那么A就会被暂停,状态变为WAITTING,进入Wait Set,我们可以称A为object的等待线程,当其他线程执行了object.notify或者notifyAll,Wait Set中的任意一个线程会被唤醒进入RUNNABLE,并且会参与和Entry Set中的线程一起争夺monitor。如果wait set的线程成功拿到了锁,这个线程就可以从Wait Set移除,否则该线程还会留在Wait set,并且再次暂停,等待下次申请锁的机会。

    线程的方法

    Thread.Sleep(long millis)

    sleep属于线程的方法,调用后当前线程进入阻塞,但是不会释放锁,millis之后,线程会苏醒进入可运行状态RUNNABLE

    Thread.yield()

    当前线程调用该方法,当前的线程就会放弃CPU,由RUNNING-->RUNNABLE,让相同优先级的线程轮流执行,但是不一定保证实际会轮流执行。当然该线程也有可能会被调度程序再次选中,yield不会导致阻塞。

    t.join/t.join(long millis)

    当前线程t1里调用其他线程t2的join方法,当前线程实际进入wait ,直到t2执行完毕或者join时间到了,t1被可能唤醒进入可运行状态。

    object.wait()

    当前线程调用object的wait方法,当前线程释放对象锁,只能等notify,notifyall唤醒或者wait时间到唤醒。

    object.notify

    唤醒在这个对象锁上面的某一个线程,notifyAll唤醒这个对象锁上面的所有线程。

  • 相关阅读:
    关于软件工程是不是教那些不会写程序的人开发软件的一些看法。
    学习这门课的一些问题
    软件测试的平台
    课程上不明白的问题
    目前流行的源程序版本管理软件和项目管理软件都有哪些, 各有什么优缺点?
    自我介绍
    【软件工程】提问回顾与个人总结
    Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解
    elasticsearch-py 解决 too_long_frame_exception 问题
    Redis集群学习笔记
  • 原文地址:https://www.cnblogs.com/dreamtaker/p/13608982.html
Copyright © 2020-2023  润新知