Java多线程的调度策略

标签: 多线程 调度策略 抢占式 协同式 时间片轮转
9676人阅读 评论(10) 收藏 举报
分类:

Java多线程环境中,为保证所有线程的执行能按照一定的规则执行,JVM实现了一个线程调度器,它定义了线程调度的策略,对于CPU运算的分配都进行了规定,按照这些特定的机制为多个线程分配CPU的使用权。这小节关注线程如何进行调度,了解了java线程调度模式有助于后面并发框架的深入探讨。

一般线程调度模式分为两种——抢占式调度和协同式调度。抢占式调度指的是每条线程执行的时间、线程的切换都由系统控制,系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会导致整个进程堵塞。协同式调度指某一线程执行完后主动通知系统切换到另一线程上执行,这种模式就像接力赛一样,一个人跑完自己的路程就把接力棒交接给下一个人,下个人继续往下跑。线程的执行时间由线程本身控制,线程切换可以预知,不存在多线程同步问题,但它有一个致命弱点:如果一个线程编写有问题,运行到一半就一直堵塞,那么可能导致整个系统崩溃。


2-5-6-1

为更加形象说明两种模式的不同,看图2-5-6-1,左边为抢占式线程调度,假如三条线程需要运行,处理器运行的路径是在线程一运行一个时间片后强制切换到线程二运行一个时间片,然后切到线程三,再回到线程一,如此循环直至三条线程都执行完。而协同式线程调度则不这样走,它会先将线程一执行完,线程一再通知线程二执行,线程二再通知线程三,直到线程三执行完。

在了解了两种线程调度模式后,现在考虑Java使用的是哪种线程调度模式。此问题的讨论涉及到JVM的实现,JVM规范中规定每个线程都有优先级,且优先级越高越优先执行,但优先级高并不代表能独自占用执行时间片,可能是优先级高得到越多的执行时间片,反之,优先级低的分到的执行时间少但不会分配不到执行时间。JVM的规范没有严格地给调度策略定义,我想正是因为面对众多不同调度策略,JVM要封装所有细节提供一个统一的策略不太现实,于是给了一个不严谨但足够统一的定义。回到问题上,Java使用的线程调度是抢占式调度,在JVM中体现为让可运行池中优先级高的线程拥有CPU使用权,如果可运行池中线程优先级一样则随机选择线程,但要注意的是实际上一个绝对时间点只有一个线程在运行(这里是相对于一个CPU来说,如果你的机器是多核的还是可能多个线程同时运行的),直到此线程进入非可运行状态或另一个具有更高优先级的线程进入可运行线程池,才会使之让出CPU的使用权,更高优先级的线程抢占了优先级低的线程的CPU

Java中线程会按优先级分配CPU时间片运行,那么线程什么时候放弃CPU的使用权?可以归类成三种情况:

  1. 当前运行线程主动放弃CPUJVM暂时放弃CPU操作(基于时间片轮转调度的JVM操作系统不会让线程永久放弃CPU,或者说放弃本次时间片的执行权),例如调用yield()方法。

  2. 当前运行线程因为某些原因进入阻塞状态,例如阻塞在I/O上。

  3. 当前运行线程结束,即运行完run()方法里面的任务。

三种情况中第三种很好理解,任务执行完了自然放弃CPU,前两种情况用两个例子说明,先看使用yield放弃CPU什么情况:

public class YeildThread {

publicstatic void main(String[] args) {

    MyThreadmt = new MyThread();

    mt.start();

    while(true) {

         System.out.println("主线程");

    }

}

}

 

class MyThread extends Thread {

publicvoid run() {

    while(true) {

         System.out.println("被放弃线程");

         Thread.currentThread().yield();

    }

}

}

截取某段输出如下,输出“主线程”比“被放弃线程”运行的机会多,因为mt线程每次循环都把时间片让给主线程,正是因为yield操作并不会永远放弃CPU,仅仅只是放弃了此次时间片,把剩下的时间让给别的线程,

主线程

主线程

主线程

主线程

主线程

主线程

被放弃线程

主线程

主线程

主线程

主线程

主线程

主线程

主线程

 

第二个例子为节省代码量将使用伪代码表示,例子简单但已能说明问题,运行程序将有两条线程工作,ioThread每次遇到I/O阻塞就放弃当前的时间片,而主线程则按JVM分配的时间片正常运行。

public class IOBlockThread {

publicstatic void main(String[] args) {

     IOThread ioThread = new IOThread();

     ioThread.start();

    主线程任务执行

}

}

 

class IOThread extends Thread {

publicvoid run() {

    while(true) {

         I/O阻塞

    }

}

}

 

Java的线程的调度机制都由JVM实现,假如有若干条线程,你想让某些线程拥有更长的执行时间,或某些线程分配少点执行时间,这时就涉及“线程优先级”,Java把线程优先级分成10个级别,线程被创建时如果没有明确声明则使用默认优先级,JVM将根据每个线程的优先级分配执行时间的概率。有三个常量Thread.MIN_PRIORITYThread.NORM_PRIORITYThread.MAX_PRIORITY分别表示最小优先级值(1)、默认优先级值(5)、最大优先级值(10)。

由于JVM的实现以宿主操作系统为基础,所以Java优先级值与各种不同操作系统的原生线程优先级必然存在某种映射关系,这样才足以封装所有操作系统的优先级提供统一优先级语义。例如1-10优先级值在linux可能要与0-99优先级值进行映射,而windows系统则有7个优先级要映射。

线程的调度策略决定上层多线程运行机制,JVM的线程调度器实现了抢占式调度,每条线程执行的时间由它分配管理,它将按照线程优先级的建议对线程执行的时间进行分配,优先级越高,可能得到CPU的时间则越长。

 


点击订购作者书籍《Tomcat内核设计剖析》


=====================================

公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。


鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以购买。感谢各位朋友。


[为什么写《Tomcat内核设计剖析》](http://blog.csdn.net/wangyangzhizhou/article/details/74080321)

==================================================




查看评论

【多线程编程】线程调度策略及优先级

通过之前的学习我知道了当前 Linux 系统下任务调度策略主要有三种: 1、SCHED_OTHER:普通任务调度策略。 2、SCHED_FIFO:实时任务调度策略,先到先服务。一旦占用cpu则一直...
  • woxiaohahaa
  • woxiaohahaa
  • 2016-09-07 20:10:21
  • 2077

浅析Linux线程调度

在Linux中,线程是由进程来实现,线程就是轻量级进程( lightweight process ),因此在Linux中,线程的调度是按照进程的调度方式来进行调度的,也就是说线程是调度单元。Linux...
  • MaximusZhou
  • MaximusZhou
  • 2014-12-20 13:47:22
  • 8451

linux线程调度策略

http://blog.csdn.net/xiaochangfu/article/details/4733679 linux进程调度方法(SCHED_OTHER,SCHED_FIFO...
  • derek_yi
  • derek_yi
  • 2014-04-19 16:48:39
  • 922

有关Linux下线程的调度

由于Linux对线程实现的一些特殊性,所以会给我们理解线程及其相关的一些函数带来疑惑,这里做个解答:  1、关于sleep  很多书籍上都说sleep这个系统调用会使整个进程sleep,而...
  • hanchaoman
  • hanchaoman
  • 2011-08-18 11:31:38
  • 11678

深入理解JVM-Java线程-实现方式,线程调度,状态

Java线程并发并不一定依赖多线程,但Java里谈论并发大多数都与线程脱不开关系。 线程是比进程更轻量级的调度执行单位,线程的引入可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(...
  • nalanmingdian
  • nalanmingdian
  • 2017-08-31 16:07:34
  • 1053

Java线程: 线程调度

线程调度是Java多线程的核心,只有好的调度,才能充分发挥系统的性能,提高程序的执行效率。 一、休眠   休眠的目的是使线程让出CPU的最简单做法,线程休眠的时候,会将CPU交给其他线程,以便轮换...
  • arredando
  • arredando
  • 2017-08-04 14:47:20
  • 1027

关于jvm的线程调度在不同操作系统下的实现说明

java的线程概念与操作系统的线程概念是不同的,java的线程概念差不多与windows线程概念一致,但是java既然目标是跨平台语言,那么它的线程机制概念是在所有平台上都是一样的,但是实际实现又不是...
  • destruction666
  • destruction666
  • 2013-03-01 17:54:13
  • 1651

Java 多线程编程之八:多线程的调度

本博客是“Java 多线程编程”系列的后续篇。“Java 多线程编程”系列其他博客请参阅本博客结尾部分。         有多个线程,如何控制它们执行的先后次序?         方法一:设置线程优先...
  • defonds
  • defonds
  • 2013-03-12 18:08:52
  • 12839

JAVA 多线程编程 --线程的调度策略

JAVA中一个线程有自己的优先级(priority) getPriority:返回当前线程的优先级 setPriority:设置当前线程的优先级   既然JAVA支持多线程,那么多个线程同时执...
  • sigangjun
  • sigangjun
  • 2015-08-19 19:59:39
  • 569
    作者
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 115万+
    积分: 1万+
    排名: 964
    博客专栏
    最新评论