原博客:http://www.blogjava.net/santicom/archive/2011/09/01/357765.html
线程的状态与调度
1,线程的生命周期
线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。
1.新建状态(New):
当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
2.就绪状态(Runnable)
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
3.运行状态(Running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4. 阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;
......
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
5. 死亡状态(Dead)
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.
2, 线程的优先级和调度
Java的每个线程都有一个优先级,当有多个线程处于就绪状态时,线程调度程序根据线程的优先级调度线程运行。
可以用下面方法设置和返回线程的优先级。
· public final void setPriority(int newPriority) 设置线程的优先级。
· public final int getPriority() 返回线程的优先级。
newPriority为线程的优先级,其取值为1到10之间的整数,也可以使用Thread类定义的常量来设置线程的优先级,这些常量分别为:Thread.MIN_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY,它们分别对应于线程优先级的1、5和10,数值越大优先级越高。当创建Java线程时,如果没有指定它的优先级,则它从创建该线程那里继承优先级。
一般来说,只有在当前线程停止或由于某种原因被阻塞,较低优先级的线程才有机会运行。
前面说过多个线程可并发运行,然而实际上并不总是这样。由于很多计算机都是单CPU的,所以一个时刻只能有一个线程运行,多个线程的并发运行只是幻觉。在单CPU机器上多个线程的执行是按照某种顺序执行的,这称为线程的调度(scheduling)。
大多数计算机仅有一个CPU,所以线程必须与其他线程共享CPU。多个线程在单个CPU是按照某种顺序执行的。实际的调度策略随系统的不同而不同,通常线程调度可以采用两种策略调度处于就绪状态的线程。
(1) 抢占式调度策略
Java运行时系统的线程调度算法是抢占式的 (preemptive)。Java运行时系统支持一种简单的固定优先级的调度算法。如果一个优先级比其他任何处于可运行状态的线程都高的线程进入就绪状态,那么运行时系统就会选择该线程运行。新的优先级较高的线程抢占(preempt)了其他线程。但是Java运行时系统并不抢占同优先级的线程。换句话说,Java运行时系统不是分时的(time-slice)。然而,基于Java Thread类的实现系统可能是支持分时的,因此编写代码时不要依赖分时。当系统中的处于就绪状态的线程都具有相同优先级时,线程调度程序采用一种简单的、非抢占式的轮转的调度顺序。
(2) 时间片轮转调度策略
有些系统的线程调度采用时间片轮转(round-robin)调度策略。这种调度策略是从所有处于就绪状态的线程中选择优先级最高的线程分配一定的CPU时间运行。该时间过后再选择其他线程运行。只有当线程运行结束、放弃(yield)CPU或由于某种原因进入阻塞状态,低优先级的线程才有机会执行。如果有两个优先级相同的线程都在等待CPU,则调度程序以轮转的方式选择运行的线程。
3. 线程状态的改变
一个线程在其生命周期中可以从一种状态改变到另一种状态,线程状态的变迁如图所示:
1> 控制线程的启动和结束
当一个新建的线程调用它的start()方法后即进入就绪状态,处于就绪状态的线程被线程调度程序选中就可以获得CPU时间,进入运行状态,该线程就开始运行run()方法。
控制线程的结束稍微复杂一点。如果线程的run()方法是一个确定次数的循环,则循环结束后,线程运行就结束了,线程对象即进入死亡状态。如果run()方法是一个不确定循环,早期的方法是调用线程对象的stop()方法,然而由于该方法可能导致线程死锁,因此从1.1版开始,不推荐使用该方法结束线程。一般是通过设置一个标志变量,在程序中改变标志变量的值实现结束线程。请看下面的例子:
程序 ThreadStop.java
class Timer implements Runnable{
boolean flag=true;
public void run(){
while(flag){
System.out.print("\r\t"+new Date()+"
![](http://www.blogjava.net/Images/dot.gif)
try{
Thread.sleep(1000);
}catch(InterruptedException e){}
}
System.out.println("\n"+Thread.currentThread().getName()+" Stop");
}
public void stopRun(){
flag = false;
}
}
public class ThreadStop{
public static void main(String args[]){
Timer timer = new Timer();
Thread thread = new Thread(timer);
thread.setName("Timer");
thread.start();
for(int i=0;i<100;i++){
System.out.print("\r"+i);
try{
Thread.sleep(100);
}catch(InterruptedException e){}
}
timer.stopRun();
}
}
该程序在Timer类中定义了一个布尔变量flag,同时定义了一个stopRun()方法,在其中将该变量设置为false。在主程序中通过调用该方法,从而改变该变量的值,使得run()方法的while循环条件不满足,从而实现结束线程的运行。
说明 在Thread类中除了stop()方法被标注为不推荐(deprecated) 使用外,suspend()方法和resume()方法也被标明不推荐使用,这两个方法原来用作线程的挂起和恢复.
2> 线程阻塞条件
处于运行状态的线程除了可以进入死亡状态外,还可能进入就绪状态和阻塞状态。下面分别讨论这两种情况:
(1) 运行状态到就绪状态
处于运行状态的线程如果调用了yield()方法,那么它将放弃CPU时间,使当前正在运行的线程进入就绪状态。这时有几种可能的情况:如果没有其他的线程处于就绪状态等待运行,该线程会立即继续运行;如果有等待的线程,此时线程回到就绪状态状态与其他线程竞争CPU时间,当有比该线程优先级高的线程时,高优先级的线程进入运行状态,当没有比该线程优先级高的线程时,但有同优先级的线程,则由线程调度程序来决定哪个线程进入运行状态,因此线程调用yield()方法只能将CPU时间让给具有同优先级的或高优先级的线程而不能让给低优先级的线程。
一般来说,在调用线程的yield()方法可以使耗时的线程暂停执行一段时间,使其他线程有执行的机会。
(2) 运行状态到阻塞状态
有多种原因可使当前运行的线程进入阻塞状态,进入阻塞状态的线程当相应的事件结束或条件满足时进入就绪状态。使线程进入阻塞状态可能有多种原因:
① 线程调用了sleep()方法,线程进入睡眠状态,此时该线程停止执行一段时间。当时间到时该线程回到就绪状态,与其他线程竞争CPU时间。
Thread类中定义了一个interrupt()方法。一个处于睡眠中的线程若调用了interrupt()方法,该线程立即结束睡眠进入就绪状态。
② 如果一个线程的运行需要进行I/O操作,比如从键盘接收数据,这时程序可能需要等待用户的输入,这时如果该线程一直占用CPU,其他线程就得不到运行。这种情况称为I/O阻塞。这时该线程就会离开运行状态而进入阻塞状态。Java语言的所有I/O方法都具有这种行为。
③ 有时要求当前线程的执行在另一个线程执行结束后再继续执行,这时可以调用join()方法实现,join()方法有下面三种格式:
· public void join() throws InterruptedException 使当前线程暂停执行,等待调用该方法的线程结束后再执行当前线程。
· public void join(long millis) throws InterruptedException 最多等待millis毫秒后,当前线程继续执行。
· public void join(long millis, int nanos) throws InterruptedException 可以指定多少毫秒、多少纳秒后继续执行当前线程。
上述方法使当前线程暂停执行,进入阻塞状态,当调用线程结束或指定的时间过后,当前线程线程进入就绪状态,例如执行下面代码:
t.join();
将使当前线程进入阻塞状态,当线程t执行结束后,当前线程才能继续执行。
④ 线程调用了wait()方法,等待某个条件变量,此时该线程进入阻塞状态。直到被通知(调用了notify()或notifyAll()方法)结束等待后,线程回到就绪状态。
⑤ 另外如果线程不能获得对象锁,也进入就绪状态。
相关推荐
Java线程调度策略包括抢占式调度和合作式调度。在Java中,默认采用的是抢占式调度,即线程的执行优先级由JVM决定,高优先级的线程可能会中断低优先级线程的执行。然而,开发者可以通过设置线程的优先级来影响调度,...
这是因为Java线程调度采用了非确定性的抢占式调度策略,即使优先级较高的线程也可能因为其他原因(如线程休眠、同步锁等)被暂时挂起,让其他线程有机会执行。 Java线程的优先级分为三个级别: 1. **NORM_PRIORITY*...
电子书相关:包含4个有关JAVA线程的电子书(几乎涵盖全部有关线程的书籍) OReilly.Java.Threads.3rd.Edition....第七章 Java线程调度例子 第八章 和同步相关的高级主题 第九章 多处理器机器上的并行化 第十章 线程组
Java线程调度策略遵循操作系统的线程调度机制,同时也提供了设置线程优先级的功能,但需要注意的是,线程的优先级仅作为提示性的信息,并不能完全保证线程的实际执行顺序。 #### 五、案例分析 假设我们需要开发一...
4. **电梯调度策略**: - 最近楼层优先:电梯选择离当前位置最近的请求前往,减少移动距离。 - 最大负载优先:电梯优先去载客最多的楼层,优化载客效率。 - 均衡策略:避免电梯频繁在同一区域停留,使得所有楼层...
高优先级线程在可运行态时更有可能被调度执行,但Java的调度策略并不保证线程一定会按照优先级顺序执行。 5. **线程同步与安全** 在多线程环境中,为了防止数据竞争和保证数据一致性,Java提供了多种同步机制: -...
在Java中,线程调度主要由JVM(Java虚拟机)进行,它提供了两种主要的调度策略:抢占式调度和合作式调度。Java默认采用抢占式调度,即JVM可以根据线程的优先级或时间片来决定哪个线程运行。线程优先级有三个级别:`...
在本文中,我们将深入探讨如何使用Java编程语言来实现一个电梯调度程序,这与操作系统中的线程调度原理相类似。电梯调度程序是一个经典的多线程问题,它涉及到资源分配、任务优先级和并发控制等核心概念。让我们逐一...
线程的状态转换是由操作系统线程调度策略控制的,比如线程可能会因为等待锁或其他资源而被阻塞。 在多线程环境中,要注意线程安全问题。多个线程访问共享资源可能导致数据不一致,需要通过同步机制(如`...
5. **线程优先级与调度**:Java允许为线程设置优先级,但这并不意味着高优先级线程一定先执行,线程调度策略取决于具体的操作系统。 6. **线程中断与停止**:正确地停止线程是一项挑战,Java提供了interrupt()方法...
但是,线程调度也依赖于操作系统的线程调度策略,因此在不同操作系统上可能有不同的行为。 5. **线程同步**:在多线程环境中,线程同步是防止数据不一致的关键。Java提供了多种同步机制,如`synchronized`关键字、`...
然而,优先级高的线程并不一定先执行,这取决于操作系统的线程调度策略。 线程池是Java中处理大量并发线程的有效工具。通过Executor框架,我们可以创建不同类型的线程池,如FixedThreadPool、SingleThreadExecutor...
3. **线程优先级**:Java线程有10个优先级,通过`setPriority()`设置,但实际调度仍取决于操作系统。 4. **线程状态**:Java线程有新建、就绪、运行、阻塞和死亡五种状态,可以通过`getState()`获取。 三、线程池...
Java线程调度是多线程编程中的关键概念,它涉及到如何有效地分配CPU时间给多个并发运行的线程。线程调度的目标是确保系统资源的高效利用和程序的公平执行。在Java中,线程调度包括了多种策略,如抢占式调度、协同式...
2. **线程生命周期**:线程有新建、就绪、运行、阻塞和死亡等不同状态,书中详细讲解了这些状态的转换以及线程调度的策略。 3. **并发控制**:书中重点讨论了Java中用于线程同步的工具,如synchronized关键字、wait...
在Java中,我们可以自定义线程调度策略通过实现`java.util.concurrent.ScheduledExecutorService`接口或使用内置的`ThreadPoolExecutor`。例如,`ScheduledThreadPoolExecutor`允许我们创建一个可以安排周期性任务...
Java线程有10个优先级(MIN_PRIORITY, NORM_PRIORITY, MAX_PRIORITY),默认优先级是NORM_PRIORITY。但是,线程优先级并不保证绝对的执行顺序,操作系统调度策略可能影响实际执行顺序。 7. join()方法: 一个线程...
然而,这并不意味着高优先级的线程一定会先执行或者执行频率更高,因为线程调度是由Java虚拟机(JVM)和操作系统的线程调度策略共同决定的。 在Java中,线程的优先级由`java.lang.Thread`类中的`Priority`枚举表示...
Java中的线程调度是基于优先级的,但也受到操作系统的调度策略的影响。Java线程的优先级可以通过`setPriority()`方法设置,但实际的调度结果取决于具体的平台和操作系统。 #### 示例 以下是一个简单的线程状态示例...
状态的转换受到线程调度策略的影响,如`start()`使线程进入可运行状态,`sleep()`、`wait()`和`join()`会让线程进入阻塞状态,而`run()`方法的执行完毕或异常会导致线程死亡。 线程调度: Java线程调度包括优先级...