[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唤醒这个对象锁上面的所有线程。

原文地址:https://www.cnblogs.com/dreamtaker/p/13608982.html