zoukankan      html  css  js  c++  java
  • 4 堆

    堆的概念

    堆上的算法

    堆的打印

    CPU优先级抢占模型

    堆是一种数组对象,与之前几章所讨论的内容稍有不同的是,堆中的元素项具有特殊的顺序结构。在本书后面将介绍树数据结构,事实上堆属于一种特殊的树,即完全二叉树。完全二叉树中除了最后一层之外树的每一层都填满。之所以将堆数据结构提前介绍,一方面是因为堆也是一种基本的数据结构。另一方面,堆的一个重要功能是能实现优先队列。上一章中我们刚讨论过队列,所以紧接着在这一章介绍堆以及利用堆实现优先队列。

     

    堆的概念

    假设r[0], r[1], …, r[n-1]是一序列元素,可以看作是数组结构。如果对于任意r[i],同时满足条件r[i] ≥ r[2*i+1]和r[i] ≥ r[2*i+2],则称该序列是一个堆,也称为最大化堆,如果把不等式中的大于号≥换成小于号≤,称为最小化堆。在下面的讨论中,所有的堆指的是最大化堆。

    可以把序列r[0],r[1],…, r[n-1]看成是按数组方式存储的一棵满二叉树,元素r[0]为树根,其它元素为树中的中间节点或叶子节点(具体定义参见树数据结构)。考虑一棵按数组方式存储的满二叉树,元素r[2*i+1]和元素r[2*i+2]是元素r[i]的左孩子和右孩子,因此在一个堆中,对于任一元素r[i],其大小一定小于等于其左右两个孩子的键值r[2*i+1]和r[2*i+2]。

    最大堆的示例

    上图二叉树表示的堆结构,将堆中元素用数组形式存放中,其存储顺序:

    最大堆用数组存储结构

    根据堆的定义,可以得出堆的一个重要性质:如果一个按数组存放的满二叉树是堆,那么堆顶元素一定是堆中所有元素中最大的元素。

    该性质可以根据堆二叉树的层数利用数学归纳法证明:

    当堆二叉树的层数为1时,堆二叉树中只有一个元素r[0],因此堆顶元素是堆中所有元素中最大的元素。

    假设对于所有的层数小于n的堆二叉树,堆顶元素的值是堆中所有元素中最大的元素。当堆二叉树的层数为n时,堆顶元素r[0]的左右子树一定是层数小于n的堆二叉树,左子树的堆顶元素是r[1],右子树的堆顶元素是r[2]。根据假设可知,r[1]是左子树中最大的元素,r[2]是右子树中最大的元素;再根据堆的定义:对于任意元素r[i]满足r[i]≥r[2* i+1],r[i] ≥r[2*i+2],可知r[0]≥r[1],r[0]≥r[2],因此,堆顶元素r[0]一定是堆中所有元素中键值最大的元素。

    堆的性质使得堆很适合从一组元素中选择最值元素(最大值或最小值)的场合,只要把所有可供选择的元素组织成一个堆,堆顶元素即为需要选择的最值元素。所以在操作系统中,常常采用堆数据结构选择优先数最小线程(通常优先数越小,对应的优先级越高),在本章的示例程序中我们将讨论并仿真这一过程。

    堆上的算法

    元素项的值发生变化时对堆的调整

    假设在堆中存在n个元素r[0], r[1], … , r[n-1],这些元素之间全部满足条件r[i]≥r[2*i+1]和r[i]≥r[2*i+2],由于某种原因,其中某个元素r[k]的值发生改变,这可能会破坏堆的结构,需要对元素序列进行相应的调整,把元素序列恢复为一个堆。

    元素r[k]的值的改变有两种可能:r[k]减小或r[k]增大。在堆中,元素r[k]的值小于等于其双亲的值r[INT((k-1)/2)],大于等于左右孩子的值r[2*i+1]和r[2*i+2],如果键值r[k]增大,它仍然大于等于左右孩子的值,但可能会大于其双亲的值,这样就破坏了堆的结构。通过在序列中把r[k]和其双亲结点相互交换,对应的操作就是在二叉树中把r[k]向上移动,直至把序列调整为一个堆,我们常把这一过程称为自低向上的堆化过程。

     

     自低向上堆化过程

    如图,(b)中第3层中第三个元素从3变为7,并且大于该节点的父节点的值4。将其上移一层,具体的做法就是将其与上一层元素互换位置,如图(c)。但此时第2层中的新改变的的节点的元素值7比它的父节点的值6还大,则还需要进一步上移,如图(d)。

    与自低向上堆化过程相对应的是第顶向下对堆化过程,。如果键值r[k]减小,它仍然小于等于其父节点的值,但可能会小于其双亲的值,这样也会破坏堆的结构。通过在序列中把r[k]和其左右节点点相互交换,对应的操作就是在二叉树中把r[k]向下移动,直至把序列调整为一个堆,这一过程就称为自顶向下堆化过程。

     

    图 堆顶元素值减小后自顶向下堆化过程

       如图,图(b)中堆顶元素项变为4,此时小于它的两个字节点的值,需要将其下移一层。下移的具体过程也是与子节点进行互换,但是该节点有有两个子节点,应该选择哪一个节点?如果选择较小的一个子节点,即值为6的节点,将其与堆顶节点互换后新的堆顶值将比它值为7的右子节点的值小,这不满足堆的定义。事实上,选择用来进行交换的子节点应为两个子节点中值较大的一个,即这里的值为7的右子节点,交换后如图(c)。交换后第二层上的新节点的值依然小于它的左子节点的值,所以需要继续下移,此时只有一个子节点,所以直接交换即可,如图(d),此时即能满足堆的定义。

     

    堆中插入元素

       跟前几章讨论的数据结构类似,堆上的算法还包括向堆中插入新的元素项和删除任意位置上的元素项。

    由于堆具有特定的结构,在插入元素项时,需要选择合适的位置插入。在具体的实现时,首先在原堆的最后添加这个元素项Insert_E(此时的顺序不满足堆的性质)并将堆的大小增加一,然后将该新元素项Insert_E逐次上移,直至满足堆的性质。其过程类似上面讨论的自底向上堆化的过程。

        如上图,在堆上插入新的值为8的新元素项,首先在原堆的最后添加新元素项,如图(b),此时不满足堆的性质。将新元素项上移两次后到达堆的第1层,此时满足堆的性质,则完成元素插入过程。

        插入元素的操作通常应用于建堆。在建堆时,首先添加第一个元素项,然后利用上述插入过程逐次见新的元素项插入。

        类似的,删除元素时,首先将堆的最后一个元素项End_E和待删除的元素项互换位置(此时的顺序不满足堆的性质)并将堆的大小减小一,然后逐次调整End_E的位置,直至满足堆的性质。这个过程类似于上面讨论的自顶向下堆化过程。

     堆中删除元素项操作过程

        如上图,将堆中第一层值为7的元素项删除,首先将该元素项与堆的最后一个元素项,即值为3的元素项交换位置(此时的顺序不满足堆的性质)并将堆的大小减小一,如图(b)。交换后的顺序不满足堆的性质,需要将节点继续下移,即图(c)~(d)。在这个例子中待删除的节点跟最后一个元素项交换后进行的是自顶向下堆化过程。

     堆中删除元素项操作过程

       再看一个例子,上图中,将值为3的元素项删除,首先将该元素项与堆的最后一个元素项,即值为6的元素项交换位置(此时的顺序不满足堆的性质)并将堆的大小减小一,如图(b)。交换后的顺序不满足堆的性质,需要将节点上移,即图(c)。

    从上面两个例子可以发现,在任意的位置删除元素项时,在待删除元素项与最后一个元素项交换位置后,为保证堆的性质,需要进行的操作可能是自顶向下堆化或者是自低向上堆化。原堆的最后一个元素项在交换位置后,其值如果大于新位置的父节点的值,则向上堆化,如图;反之,如果其值小于其父节点的值,且小于其子节点的值,那么需要将节点向下堆化,如图。一个特殊的情况,在删除堆顶处元素项时,交换位置后如果需要,则进行的操作一定是自顶向下的堆化过程。

    打印堆

    在前几章中,打印线性表、栈和队列中元素项时,元素项一般是顺序打印出来的。堆具有二维结构,而且具有特殊的顺序,在打印时应将堆的结构也显示出来。在后面章节中讨论树数据结构时我们会详细介绍树的遍历算法,这里简单介绍如何打印堆中元素并显示出堆的结构性。

    在利用System.out.println函数打印字符串时,必须遵循从左到右、从上到下的顺序。可以采用层序遍历算法遍历打印每个元素项,但是难以控制各层元素之间的间隔。这里介绍基于中序遍历的算法打印堆逆时针旋转90度后的结果。这里中序遍历与我们通常采用的方法有一点不同,遍历的顺序是右子节点→父节点→左子节点。

    如图,中序遍历堆中元素得到的元素项的输出顺序为4→7→5→9→4→6→3。如果将堆旋转90度进行观察,各个元素项可以看成分别处于不同层上,可以逐行将各元素项打印出来。当然,在打印时只要根据元素项所处的层数控制打印位置。例如第一个元素项4,处于第二层,则打印在第一行第二个制表位(一个制表位即一个TAB间隔)。第九个元素项7,处于第零层,则打印在第三行第零个制表位。中为打印结果显示。

     中序遍历最大堆

    最大堆ADT的设计

       根据上面对堆数据结构的讨论,堆的操作包括堆中元素项的插入和删除,自顶向下堆化和自低向上堆化等,堆的抽象数据类型设计如下:

    package Heap;

    import Element.ElemItem;

    /** 
     * 堆数据结构 Heap.java
     *
     
    */
    public interface Heap {
        public boolean insert(ElemItem elem);//堆中合适的位置插入新的元素项
        public ElemItem remove(int position);//删除position位置的元素项
        public ElemItem removeMax();            //删除堆中最大或最小元素项
        public boolean exchange(int i, int j);//交换位置i和位置j处的两个元素
        public void shiftdown(int k);            //自顶向下堆化
        public void shiftup(int k);            //自底向上堆化
        public ElemItem topVal();                //返回堆顶元素项
        public int heapSize();                    //返回当前堆中元素项的个数
        public int leftchild(int position);    //获取position的左子节点位置
        public int rightchild(int position);    //获取position的右子节点位置
        public int parent(int position);        //获取position的父节点位置
        public boolean isleaf(int position);    //判断当前节点是否是叶节点
        public void printHeap();                //打印当前堆中所有元素
    }

    其中removeMax删除堆顶元素项,并在删除后返回该元素项。Exchange交换两个位置上的元素项,若交换成功则返回ture,否则返回false。Shiftdown和shiftup分别是自顶向下和自低向上堆化操作。topVal返回堆顶最大元素项。isleaf判断节点是否是叶节点,若是则返回ture,否则返回false。

    最大堆ADT的实现

    根据上面的讨论对最大堆类的实现如下:

    package Heap;

    import Element.ElemItem;

    /**
     * 最大堆类,MaxHeap.java
     
    */
    public class MaxHeap implements Heap{
        protected  ElemItem maxheapdata[];    //数组形式保存堆中元素项 
        protected  int maxSize;                //堆的最大大小,对应数组的最大长度
        protected  int currSize;            //堆当前有效大小
        public MaxHeap(int _MaxSize){        //构造函数
            maxSize = _MaxSize;                //最大元素个数
            currSize = 0;                    //当前有效元素个数
            maxheapdata = new ElemItem[maxSize];//新建数组,个数为最大元素个数
        }
        // 比较堆元素项大小的私有函数。
        protected int compare(int i, int j){
            return (maxheapdata[i]).compareTo(maxheapdata[j]);
        }
        
        public boolean insert(ElemItem elem) {
            if(currSize >= maxSize){
                System.out.println("堆已满!");
                return false;
            }
            else{
                int n = currSize++;        //n为堆的最后一个的位置
                maxheapdata[n] = elem;    //将元素插入
                shiftup(n);                //自底向上堆化
            }
            return true;
        }

        public ElemItem remove(int position) {
            if(position < 0 || position >= currSize){
                System.out.println("当前位置无效!");
                return null;
            }
            else{
                //将最后一个元素与position上的元素交换
                exchange(position, currSize - 1);
                //当前个数减1,currSize位置上元素即堆最后一个的元素项
                currSize--;    
                // 如果比父节点的值更大,则向上堆化
                if(1 == compare(position, parent(position))) 
                    shiftup(position);
                else shiftdown(position);//向下堆化
            }
            return maxheapdata[currSize];//返回删除的元素
        }

        public ElemItem removeMax() {
            if(currSize <= 0){
                System.out.println("当前堆为空!");
                return null;
            }
            else{
                //将最后一个元素与0位置上的元素交换
                exchange(0, currSize - 1);
                //当前个数减1,则currSize位置上元素即堆最后一个的元素项
                currSize--;        
                if(currSize > 0) shiftdown(0);//从堆顶开始自顶向下堆化
            }
            return maxheapdata[currSize];
        }

        public boolean exchange(int i, int j) {
            if( i < 0 || i >= currSize || j < 0 || j >= currSize){
                System.out.println("待交换位置不合法");
                return false;
            }
            else{
                // 交换i和j位置上的两个元素项
                ElemItem tmp = maxheapdata[i];
                maxheapdata[i] = maxheapdata[j];
                maxheapdata[j] = tmp;
            }
            return false;
        }

        public void shiftdown(int k) {
            if(k < 0 || k >= currSize){
                System.out.println("当前位置" + k + "不合法");
                return;
            }
            while(!isleaf(k)){
                int l = leftchild(k);
                if(l <= currSize - 2 && compare(l, l + 1) == -1)
                    l++;    // l是两个子节点中较大的元素项的标号
                if(compare(k, l) != -1) return;        //结束
                else{
                    exchange(l, k);    //交换
                    k = l;            //下移
                }
            }
        }

        public void shiftup(int k) {
            if(k < 0 || k >= currSize){
                System.out.println("当前位置" + k + "不合法");
                return;
            }
            int i;
            //k不是堆顶而且k处小于其父节点的值,则不断上移
            while(k >= 0 && compare(parent(k), k) == -1){
                exchange(k, parent(k));
                k = parent(k);
            }
        }

        
        public int heapSize() {
            return currSize;
        }

        public int leftchild(int position) {
            // 位置不合法:小于0或者没有子节点
            if(position < 0 || position >= currSize / 2)
                return -1;
            return 2 * position + 1;
        }

        public int rightchild(int position) {
            //位置不合法:小于0或者没有子节点
            if(position < 0 || position >= (currSize - 1) / 2)
                return -1;
            return 2 * position + 2;
        }

        public int parent(int position) {
            //位置不合法
            if(position < 0)
                return -1;
            return (int)((position - 1) / 2);
        }
        //判断是否是子节点
        public boolean isleaf(int position) {
            return position >= 0 && position >= currSize / 2;
        }

        public ElemItem topVal() {
            if(currSize > 0)return maxheapdata[0];
            return null;
        }
        
        //在特定高度打印一个元素项
        protected void printnode(int ps, int h){
            for(int i = 0; i < h; i++)
                System.out.print("\t");
            System.out.println(maxheapdata[ps].getElem());
        }
        //中序遍历堆中元素项
        protected void iterative_show(int pos, int h){
            if(pos < 0 || pos >= currSize) return;
            //先访问右子节点
            iterative_show(rightchild(pos), h + 1);
            //访问父节点
            printnode(pos, h);
            //访问左子节点
            iterative_show(leftchild(pos), h + 1);
        }
        
        public void printHeap() {
            System.out.println("堆中元素旋转90度分层打印:");
            //从第一个元素开始打印,它处于第0层
            iterative_show(0, 0);
        }
    }

        最大堆中访问当前元素项的左子节点、右子节点和父节点的操作都是复杂度为O(1)的操作。判断节点是否是叶节点的依据是,position是否大于或等于当前堆中元素项总个数的一半,这一结论可以根据后面章节中二叉树的性质很容易得到:如果堆中有n个内节点,则一定有n或n+1个外节点,即叶节点。

        通过对最大堆的实现,我们可以分析出两种堆化算法和插入删除算法的复杂度。后两种算法实质上也涉及了两种堆化算法。堆化是不断将某元素项与其子节点或父节点交换位置的过程,其交换次序最多为堆的层数,则这四个函数的复杂度都是Θ(logn)。建堆实际上是n次插入过程,则其复杂度为Θ(nlogn)

        下面通过示例程序来进一步说明对数据结构:

    package Heap;

    import Element.ElemItem;

    /**
    * 最大堆的测试实例代码,ExampleMaxHeap.java
     
    */
    public class ExampleMaxHeap {
        public static void main(String args[]){
            MaxHeap mheap = new MaxHeap(20);
            //建堆
            for(int i = 0; i < 12; i++){
                mheap.insert(new ElemItem<Integer>(i));
            }
            mheap.printHeap();
            //插入元素项值为20
            mheap.insert(new ElemItem<Integer>(20));
            System.out.println("插入元素项20后:");
            mheap.printHeap();
            ElemItem e = mheap.removeMax();
            System.out.println("删除最大项后:");
            System.out.println("删除的堆顶为:" + e.getElem());
            mheap.printHeap();
            mheap.insert(new ElemItem<Integer>(8));
            System.out.println("插入元素项8后:");
            mheap.printHeap();
            System.out.println("删除位置4上的元素项后:");
            mheap.remove(4);
            mheap.printHeap();
        }
    }

       本实例程序中首先建立最大容量为20的最大堆,并将整型常量0~11插入到堆中完成建堆过程。在此基础上先后插入元素项20,删除堆中最大元素项,此时堆中元素项与建堆时相同。最后插入元素项8,删除第4个位置上的元素项。本示例程序的运行结果如下:


    堆中元素旋转90度分层打印:
            4
        10
            5
                1
    11
                7
            8
                2
        9
                3
            6
                0
    插入元素项20后:
    堆中元素旋转90度分层打印:
            4
        11
                5
            10
                1
    20
                7
            8
                2
        9
                3
            6
                0
    删除最大项后:
    删除的堆顶为:20
    堆中元素旋转90度分层打印:
            4
        10
            5
                1
    11
                7
            8
                2
        9
                3
            6
                0
    插入元素项8后:
    堆中元素旋转90度分层打印:
            4
        10
                5
            8
                1
    11
                7
            8
                2
        9
                3
            6
                0
    删除位置4上的元素项后:
    堆中元素旋转90度分层打印:
            4
        10
            8
                1
    11
                5
            7
                2
        9
                3
            6
                0

     

          优先队列是堆的一个具体应用,实际上说成“堆是优先队列的一种自然实现方法”更能体现出问题是实质。为每个元素项设定特定的优先级,并将它们建立成堆结构。每次操作都删除并返回堆中最大的元素项,对其进行相关处理。在删除的同时还会有新的元素项不断插入堆中。

    在上一章中我们讨论了生产者-消费者模型,并用顺序队列对这个模型进行了仿真说明。这里我们将讨论操作系统中的另一个重要的问题,即多进程间的抢占。

    在操作系统中多个进程常常会同时向CPU发出处理请求,但是CPU一次只能处理一个进程。操作系统中不同进程的优先级是不同的,在进程池中等待CPU处理的进程并不一定是按照先来先处理的准则接受CPU的处理的。现代操作系统一般都支持进程之间的抢占,即后来的进程如果优先级比较高,则先处理。根据这一准则可以将进程池用优先队列结构(最大堆)来表示,优先队列的元素项即为各个优先级不同进程。CPU每隔一段时间处理进程池中的一个进程,这对应着删除优先队列顶部元素的操作。在CPU处理的同时会有新的进程添加到进程池中,新添加的进程需要在进程池中进行位置的调整,对应的优先队列中元素项的插入和自低向上堆化的过程。

    根据讨论我们将利用最大堆仿真上述的多进程抢占过程。首先需要设计进程类,这里主要讨论类的优先级,不考虑进程的具体功能。类的成员变量包括进程号、进程优先级和进程所需的CPU处理时间。其中最后一项进程所需的CPU处理时间是专为本仿真过程设定的成员变量,实际的进程肯定是没有这个属性的,因为处理时间只有在CPU完成该进程的处理任务之后才能得知。由于进程之间存在优先级的比较,所以进程类在实现Comparable接口的函数compareTo函数时比较两个进程的优先级。

    进程类的设计如下:

        package Heap;
        
        /**
        * CPU 进程类,
         
    */
        public class Process implements Comparable{
            int id;            // 进程的ID
            int priory;        //进程的优先级
            int processtime;//进程的处理时间
            int cometime;    //进程的到达时间
            public Process(){}
            
            // 比较进程的优先级
            public int compareTo(Object o) {
                Process p =(Process)(o);
                // 较高,返回1
                if(this.priory > p.priory) return 1;
                // 相同,返回0
                else if(this.priory == p.priory) return 0;
                // 较低,返回-1
                else return -1;
            }
            
        }

     

          首先设定进程的有限级分为三级:0,1,2,数值越大其优先级越高。实际操作系统中一般采用数值越小优先级越高的限定。这里之所以这样设定,主要是因为考虑到优先队列是基于最大堆实现的。

    下面将对CPU的处理速度以及新进程进入进程池的到达时间建立模型。CPU处理速度也可以用处理时间来描述,这里假设CPU对进程的处理时间是随机的,假设为8,9,10个时间单位,且均匀分布。

    进程到达进程池的时间间隔可以建模成泊松分布,即

     

    其中λ表示平均到达时间间隔,P(X=k)表示实际到达时间间隔为k的概率。

    这里假设进程到达时间间隔平均为5个时间单位,小于进程处理时间。所以进程池中处于等待状态的进程数量将逐渐增多。我们关心的问题是不同优先级的进程的等待时间之间的差异。

    进程池中进程在各个时间点既有删除也有插入,这是一个动态变化的过程。对这类仿真问题通常采用离散时间仿真方法。具体流程如图

    在流程图中多处涉及到CPU的状态,CPU的状态可以用一个变量processcnt表示,其含义是CPU正在处理的进程距离进程结束还需要处理的时间单位个数,即“CPU正在处理的进程的处理时间”。该变量为正值时表示CPU正在处理中,为零时表示当前进程处理结束,为负值表示当前CPU处于空闲状态。当进程池中有进程被删除(被CPU调用)时,Processcnt的值被更新为该进程的处理时间。

    仿真最终得到的结果是三种优先级各自的等待时间的变化情况。每当CPU处理完一个进程则打印当前三种优先级的等待时间。

     利用优先队列仿真多进程抢占CPU过程

    本示例程序代码如下:

        package Heap;
        
        import Element.ElemItem;
        
        /**
        * 进程抢占CPU的示例程序,CPUWaitTime.java
         
    */
        public class CPUWaitTime {
            //进程进入进程池的时间
            static int cometime[]={6, 10, 18, 23, 31, 35, 41, 45, 
                49, 52, 55, 60, 65, 70, 78, 83, 89, 92, 98, 104, 
                110, 117, 122, 124, 130, 136, 140, 144, 151, 156,
                161, 165, 168, 171, 176, 180, 184, 189, 195, 203,
                207, 215, 219, 225, 231, 236, 242, 246, 247, 253,
                257, 263, 268, 270, 276, 281, 287, 295, 303, 309,
                315, 321, 323, 326, 333, 341, 345, 352, 354, 360,
                363, 368, 370, 372, 376, 383, 388, 393, 401, 403,
                409, 415, 421, 424, 430, 433, 437, 442, 448, 454,
                462, 470, 475, 482, 486, 495, 501, 506, 511, 520};
            //设置三个进程优先级,数值越大优先级越大
            private static int pry[] = {0, 1, 2};
            
            public static void main(String args[]){
                Process pc[];// = new Process[100];
                pc = new Process[100];
                for(int i = 0; i < 100; i++){
                    pc[i] = new Process();
                    //进程号
                    pc[i].id = i;
                    //随机生成进程优先级,等概率生成
                    double dr = Math.random();
                    int r = (dr <= 0.33)?0:((dr <=0.66)?1:2);
                    pc[i].priory = pry[r];
                    //设置该进程进入进程池的时间
                    pc[i].cometime = cometime[i];
                    //随机生成进程的处理时间,等概率生成8,9,10
                    dr = Math.random();
                    r = (dr <= 0.33)?8:((dr <=0.66)?9:10);
                    pc[i].processtime = r;
                }
                
                int waittime[] = {0, 0, 0};
                int cnt = 0; int idx = 1;
                int processcnt = pc[0].processtime;
                MaxHeap mheap = new MaxHeap(100);
                mheap.insert(new ElemItem<Process>(pc[0]));
                while(idx <= 100){
                    // 时间递增1
                    cnt++;
                    // 正在处理的进程的处理时间递减1
                    if(processcnt > 0) processcnt--;
                    // 如果正在处理的进程处理完毕
                    if(processcnt == 0){
                        //删除优先队列顶部进程(进入CPU处理)
                        ElemItem e = mheap.removeMax();    
                        //更新刚刚删除的进程的优先级对应的等待时间
                        waittime[((Process)e.getElem()).priory]
                         += cnt-((Process)e.getElem()).cometime;
                        System.out.println(waittime[0]);
                        System.out.println(waittime[1]);
                        System.out.println(waittime[2]);
                        // 获取当前优先队列列顶部的进程
                        e = mheap.topVal();
                        //如果此时还没有进程在进程池中,则将
                        
    //当前CPU正在处理的进程的处理时间设为-1
                        if(e == null){
                            processcnt = -1;
                        }
                        //将优先队列列顶的新进程的处理时间作为
                        
    //当前CPU正在处理的进程的处理时间
                        else 
                            processcnt = 
                              ((Process)e.getElem()).processtime;
                    }
                    // 进程进入进程池
                    if(idx < 100 && cnt == cometime[idx]){
                        mheap.insert(new ElemItem<Process>(pc[idx]));
                        //如果进入的进程是第一个,则设置当前CPU
                        
    //正在处理的进程的处理时间
                        if(mheap.heapSize() == 1) 
                            processcnt = pc[idx].processtime;
                        idx++;
                    }
                    // 100个进程都进入了进程池并且此时进程堆也空,过程结束
                    if(idx == 100 && mheap.heapSize() == 0) break;
                }
            }    
        }

    各优先级进程在CPU中的总等待时间如图。这里统计的是总时间,由于各个进程的优先级是等概率随机赋值的,所以总时间与平均时间是一致的。

    从图中的曲线可以发现,优先级高的进程的等待时间很少,而优先级低的进程一开始几乎无法获得CPU的使用权。图中0~t1之间优先级为0的进程几乎没有获得CPU的使用权,优先级为1的进程被CPU处理的次数也很少,CPU几乎全在处理优先级为2 的进程。t1~t2之间优先级为2的进程全部处理完成,此时CPU几乎全在处理优先级为1的进程,而优先级为0的进程依然无法获得CPU的调用。t2到整个过程结束这段时间CPU才开始处理优先级最低的进程。从图中三条曲线可以很清楚地看出,在抢占模式下,优先级高的进程将获得更快的处理。

    这里需要解释的是,本示例只是用来形象地描述优先队列的一个应用,但它与实际的操作系统的中机制差别很大。实际的操作系统中不同优先级的进程所占的比例是不同的,通常优先级越高的进程所占的比例越低。此外,现代操作系统也采用多进程机制和多核机制来并行处理多个进程,各优先权的数量的均等性和到达时间的序性并没有本示例中明确。

     
  • 相关阅读:
    mongoDB常用命令
    Linux下安装MongoDB
    Linux下mysq基础命令(二)
    Linux下mysql基础命令(一)
    Linux 下使用yum 命令安装MySQL
    Linux 常用命令
    windows7用WMware安装Linux虚拟机详细步骤
    接口测试全流程扫盲
    Jmeter时间格式化
    Jmeter之测试报告
  • 原文地址:https://www.cnblogs.com/luweiseu/p/2590972.html
Copyright © 2011-2022 走看看