在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. 当前运行线程主动放弃CPU,JVM暂时放弃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_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY分别表示最小优先级值(1)、默认优先级值(5)、最大优先级值(10)。

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

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

喜欢研究java的同学可以交个朋友,下面是本人的微信号:

Java多线程的调度策略的更多相关文章

  1. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  2. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  3. java多线程(五)之总结(转)

    引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...

  4. Java多线程编程实战指南(核心篇)读书笔记(二)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  5. Java多线程编程实战指南(核心篇)读书笔记(一)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76422930冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  6. java 多线程超详细总结——阿里大牛熬夜整理

    引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...

  7. 面试题: java多线程 背1

    如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...

  8. Java 多线程并发编程一览笔录

    Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...

  9. Java多线程——线程的优先级和生命周期

    Java多线程——线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...

随机推荐

  1. jQuery的ajax学习

    jQuery是一个非常常见的JavaScript库,可是,突然发现其实自己大多数时候,潜意识里面对它其实是视而不见的,比如它的ajax,不是没用过,每次使用,要不,是直接套用已有的格式,要不直接从官网 ...

  2. 简陋的个人Vim使用命令

    最近把Visual Studio 的编辑器改成了 Vim,感觉像发现了新世界,记录记录一些提高效率的Vim命令. 插入命令 i 在当前位置前插入 I 在当前行首插入 a 在当前位置后插入 A 在当前行 ...

  3. 【BZOJ1026】【SCOI2009】windy数

    Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? In ...

  4. JavaTCP和UDP套接字编程

    在我们刚开始入门Java后端的时候可能你会觉得有点复杂,包含了很多杂七杂八的知识,例如文件上传下载,监听器,JDBC,请求重定向,请求转发等等(当然也没有很多),但是我们自己真正的去开发一个小型网站( ...

  5. 【docker简易笔记】docker基础信息的分享

    docker 使用的频率越来越高,所以在后续的一些博客中会分享一些docker的安装和使用. 一.docker介绍   "Docker 最初是 dotCloud 公司创始人 Solomon ...

  6. 备忘:MySQL中修改表中某列的数据类型、删除外键约束

    -- MySQL中修改表中某列的数据类型 ALTER TABLE [COLUMN] 表名 MODIFY 列名 列定义; -- 删除外键约束 SHOW CREATE TABLE 表名; -- 复制CON ...

  7. JAVA解析XML文件(DOM,SAX,JDOM,DOM4j附代码实现)

    1.解析XML主要有四种方式 1.DOM方式解析XML(与平台无关,JAVA提供,一次性加载XML文件内容,形成树结构,不适用于大文件) 2.SAX方式解析XML(基于事件驱动,逐条解析,适用于只处理 ...

  8. jquery easyui datagrid设置行样式 不可删除某行

    rowStyler: function (index,row) { if (parseInt(row.ksrs) > 0) { return 'color:red'; } }, onLoadSu ...

  9. 深入理解Lambda函数及其用法

    Lambda函数又称匿名函数,匿名函数就是没有名字的函数,函数没有名字也行?当然可以啦.有些函数如果只是临时一用,而且它的业务逻辑也很简单时,就没必要非给它取个名字不可. 先来看个简单lambda函数 ...

  10. 利用Python进行数据分析——重要的Python库介绍

    利用Python进行数据分析--重要的Python库介绍 一.NumPy 用于数组执行元素级计算及直接对数组执行数学运算 线性代数运算.傅里叶运算.随机数的生成 用于C/C++等代码的集成 二.pan ...