1.3线程状态
线程可以有以下4个状态:
1.3.1 新生线程
当你用new操作符创建一个线程时,例如用new Thread(r),线程还没有开始运行。此时线程处在New——新生状态。当一个线程处在新生状态时,程序还没有开始运行线程中的代码。在线程可以运行之前,还有一些簿记工作要做。
1.3.2 可运行线程
一旦调用了start方法,该线程就成为可运行——Runnable的了。一个可运行线程可能实际上正在运行,也可能没有,这取决于操作系统为该线程提供的运行时间。(不过,Java规范并没有把这种情况作为一种单独的状态,一个正在运行的线仍然处于可运行状态。)
注意:可运行状态和Runnable接口没有任何关系。
一旦开始运行,它不需要始终保持运行。事实上,线程在运行过程中有时需要被中断,目的是使其他线程获得运行的机会。线程调度的细节取决于操作系统所提供的服务。使用抢占式调度的系统给每个可运行的线程一个时间片来处理任务。当这个时间片用完时,操作系统剥夺该线程对资源的占用,使其他的线程有机会运行。在选择下一个线程时,操作系统会考虑到线程的优先级。
所有现代的桌面和服务器系统都使用抢占式调度。但一些小型设备,例如手机,可能会采用协作式调度。这样的设备中,一个线程只有在调用sleep或yield这样的方法时才会丢失控制权。
在一个多处理器的机器上,每个处理器都可以运行一个线程,你可以使多个线程并行运行。当然,如果线程个数大于处理器个数,那么调度器将仍旧采用时间片机制。
记住,在任何给定的时刻,一个可运行线程可能在运行,也可能不是。(这正是为什么把这个状态称为“runnable”而不是“running”。)
1.3.3 被阻塞线程
当发生以下任何在一种情况时,线程就进入被阻塞状态:
当一个线程被阻塞(或者,当他死亡时),另一个线程就可以被调度运行了。当一个被阻塞的线程重新被激活时(例如,由于它的睡眠时间已经到达了指定的毫秒数或因为它所等待的I/O已经完成),调度器检查他的优先级是否高于当前的运行线程,如果是,它就将抢占当前线程的资源并开始运行。
通过以下几种途径中的一种,线程可以从被阻塞状态回到可运行状态。
提示:如果你需要解除I/O操作的阻塞状态,应该使用“new I/O”库中的通道(channel)机制。当另一个线程关闭通道时,被阻塞的线程就再次变为可运行的了,而阻塞操作将抛出一个ClosedChannelException异常。
1.3.4 死线程
有两个原因会导致线程死亡:
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果线程是可运行的或者被阻塞的,这个方法返回true;否则,如果线程仍旧处于new状态且不是可运行的,或者线程死亡了,就返回false;
注意:无法确定一个活着的线程是可运行的还是被阻塞了,也无法确定一个可运行的线程是否正在运行。另外,你也无法区分线程死亡和非可运行线程。
API java.lang.Thread 1.0线程可以有以下4个状态:
- New —— 新生
- Runnable —— 可运行
- Block —— 被阻塞
- Dead —— 死亡
1.3.1 新生线程
当你用new操作符创建一个线程时,例如用new Thread(r),线程还没有开始运行。此时线程处在New——新生状态。当一个线程处在新生状态时,程序还没有开始运行线程中的代码。在线程可以运行之前,还有一些簿记工作要做。
1.3.2 可运行线程
一旦调用了start方法,该线程就成为可运行——Runnable的了。一个可运行线程可能实际上正在运行,也可能没有,这取决于操作系统为该线程提供的运行时间。(不过,Java规范并没有把这种情况作为一种单独的状态,一个正在运行的线仍然处于可运行状态。)
注意:可运行状态和Runnable接口没有任何关系。
一旦开始运行,它不需要始终保持运行。事实上,线程在运行过程中有时需要被中断,目的是使其他线程获得运行的机会。线程调度的细节取决于操作系统所提供的服务。使用抢占式调度的系统给每个可运行的线程一个时间片来处理任务。当这个时间片用完时,操作系统剥夺该线程对资源的占用,使其他的线程有机会运行。在选择下一个线程时,操作系统会考虑到线程的优先级。
所有现代的桌面和服务器系统都使用抢占式调度。但一些小型设备,例如手机,可能会采用协作式调度。这样的设备中,一个线程只有在调用sleep或yield这样的方法时才会丢失控制权。
在一个多处理器的机器上,每个处理器都可以运行一个线程,你可以使多个线程并行运行。当然,如果线程个数大于处理器个数,那么调度器将仍旧采用时间片机制。
记住,在任何给定的时刻,一个可运行线程可能在运行,也可能不是。(这正是为什么把这个状态称为“runnable”而不是“running”。)
1.3.3 被阻塞线程
当发生以下任何在一种情况时,线程就进入被阻塞状态:
- 线程通过调用sleep方法进入睡眠状态;
- 线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
- 线程试图得到一个锁,而该锁正被其他线程持有。
- 线程在等待某个触发条件;
- 有人调用了线程的suspend方法。不过这个方法已经过时了,而你不应该在你的代码中调用它。
当一个线程被阻塞(或者,当他死亡时),另一个线程就可以被调度运行了。当一个被阻塞的线程重新被激活时(例如,由于它的睡眠时间已经到达了指定的毫秒数或因为它所等待的I/O已经完成),调度器检查他的优先级是否高于当前的运行线程,如果是,它就将抢占当前线程的资源并开始运行。
通过以下几种途径中的一种,线程可以从被阻塞状态回到可运行状态。
- 线程被置于睡眠状态,且已经经过指定的毫秒数;
- 线程正在等待I/O操作完成,且该操作已经完成;
- 线程正在等待另一个线程所持有的锁,且另一个线程已经释放该锁的所有权;(也有可能等待超时,当超时发生时,线程解除阻塞。)
- 线程正在等待某个触发条件,且另一个线程发出了信号表明已经发生了变化。(如果为线程的等待设置了一个超时,那么当超时发生时线程将解除阻塞。)
- 线程已被挂起,且有人调用了它的resume方法。不过,由于suspend方法已经过时,resume方法也就随之被弃用了,你不应该在自己的代码中调用它。
提示:如果你需要解除I/O操作的阻塞状态,应该使用“new I/O”库中的通道(channel)机制。当另一个线程关闭通道时,被阻塞的线程就再次变为可运行的了,而阻塞操作将抛出一个ClosedChannelException异常。
1.3.4 死线程
有两个原因会导致线程死亡:
- 因为run方法正常退出而自然死亡;
- 因为一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果线程是可运行的或者被阻塞的,这个方法返回true;否则,如果线程仍旧处于new状态且不是可运行的,或者线程死亡了,就返回false;
注意:无法确定一个活着的线程是可运行的还是被阻塞了,也无法确定一个可运行的线程是否正在运行。另外,你也无法区分线程死亡和非可运行线程。
- boolean isAlive( ) 如果线程已经启动并且还没有被禁止,则返回true;
- void stop( ) 停止线程。这个方法已经过时了。
- void suspend( ) 挂起当前线程的执行过程。这个方法已经过时了。
- void resume() 恢复线程。这个方法只能在调用suspend() 之后调用。这个方法已经过时了。
- void join() 等待直到指定的线程死亡。
- void join(long millis) 等待直到指定的线程死亡或经过指定的毫秒数。