线程的生命周期
- NEW(新建状态)
- RUNNABLE(就绪状态)
- RUANNABLE(运行状态)
- BLOCKED(阻塞)
- TERMINATED(死亡状态)
图解:
新建状态
线程对象被创建后,就进入了新建状态。例如:
Thread thread = new Thread();
当程序使用new关键字创建一个线程之后,该线程就处于新建状态,此时它和其他的Java对象一样,仅仅由Java虚拟机与其分配内存,并初始化其成员变量的值,此时线程对象没有表现出任何线程的动态特征,程序也不会执行线程的执行体。
就绪状态
当线程对象调用了start()方法之后,该线程处于就绪状态,java虚拟机会为其创建方法调用栈和程序计数器,处于这个状态中的线程并没有开始运行,知识该程序可以运行了。
注意:
1.如果失去处理器或者调用yield()(礼让线程),就会变成就绪状态。
2.阻塞状态满足解除阻塞状态的条件,就会变成就绪状态。
运行状态
一个处理器(CPU),在同一时间内,只能处理一个任务。
当一个线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。
注意:线程只能从就绪状态进入到运行状态。
线程怎么获得CPU?
线程调度的细节取决于底层平台所采用的策略。对于采用抢占式策略的系统而言,系统会给每个可执行的线程一个小时间段来处理任务;当该时间段用完后,系统就会剥夺该线程所占用的资源,让其他线程获得执行的机会。在选择下一个线程时,系统会考虑线程的优先级。
阻塞状态
阻塞状态是线程因为某种原因放弃cpu使用权,暂时停止运行。直到线程引入就绪状态,才有机会转到运行状态。
阻塞的情况分为三种:
- 等待阻塞—通过调用线程的==wait()==方法,让线程等待某工作的完成。
- 同步阻塞—线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
- 其他阻塞—通过调用线程的==sleep()或join()==或发出了I/O请求,线程会进入阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态
如下三种方式线程死亡:
- run()或call()方法执行完成,线程正常结束。
- 线程抛出一个未捕获的Exception或Error;
- 直接调用stop()方法来结束该线程–该方法容易造成死锁,不推荐。
sleep()和wait()方法的区别
sleep()方法
sleep()方法是Thread类的方法,通过其定义可知是个native方法,在指定的时间内阻塞线程的执行。而且从其注释中可知,并不会失去对任何监视器(monitors)的所有权,也就是说不会释放锁,仅仅会让出cpu的执行权。
wait()方法
wait()方式是基类Object的方法,其实也是个native方法。
不管是wait()还是wait(long timeout, int nanos),其调用的都是wait(long timeout)。
根据注释中的一句话,可以看出此方法调用的前提是当前线程已经获取了对象监视器monitor的所有权。
该方法会调用后不仅会让出cpu的执行权,还会释放锁(即monitor的所有权),并且进入wait set中,知道其他线程调用notify()或者notifyall()方法,或者指定的timeout到了,才会从wait set中出来,并重新竞争锁。
区别
最主要的区别就是释放锁(monitor所有权与否),但是两个方法都会抛出InterruptedException。
线程阻塞BLOCKED和等待WAITING的区别
阻塞BLOCKED
阻塞表示线程在等待对象的monitor锁,试图通过synchronized去获取某个锁,但是此时其他线程已经独占了monitor锁,那么当前线程就会进入等待状态。
等待WAITING
当前线程等待其他线程执行某些操作,典型场景就是生产者消费者模式,在任务条件不满足时,等待其他线程的操作从而使得条件满足。可以通过wait()方法或者Thread.join()方法都会使线程进入等待状态。
ps:
实际上不用可以区分两者, 因为两者都会暂停线程的执行. 两者的区别是: 进入waiting状态是线程主动的, 而进入blocked状态是被动的. 更进一步的说, 进入blocked状态是在同步(synchronized代码之外), 而进入waiting状态是在同步代码之内