Java多线程的调度策略

Posted yutingliuyl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程的调度策略相关的知识,希望对你有一定的参考价值。

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内核设计剖析》





以上是关于Java多线程的调度策略的主要内容,如果未能解决你的问题,请参考以下文章

java多线程中的调度策略

Java多线程之线程生命周期

JAVA多线程(十四)模式-Active Object

java 多线程——quartz 定时调度的例子

python之进程线程协程的调度原理

Java多线程-线程调度及获取和设置线程优先级