精华内容
下载资源
问答
  • 前文说过,子进程创建之后,父子进程究竟谁先运行是由调度器说了算。 但是,谁先结束呢?一般来说肯定是要让子进程先结束,想一想我们的bash,bash是所有命令的父进程,你总不能让bash先挂吧 其实之所以让子进程先...


    前文说过,子进程被创建之后,父子进程究竟谁先运行是由调度器说了算。
    但是,谁先结束呢?一般来说肯定是要让子进程先结束,想一想我们的bash,bash是所有命令的父进程,你总不能让bash先挂吧
    其实之所以让子进程先退出,是因为父进程容易对子进程进行管理,而且子进程被创建的原因就是因为父进程想要让子进程帮助自己完成一些业务,因此它要拿到结果。因此父进程一般需要等待子进程

    (1)为什么子进程需要被等待

    既然子进程要先退出,那么子进程退出后就变成了僵尸状态,一旦变成僵尸状态,这个子进程就如同僵尸一样,杀也杀不死(因为它已经死了),所以它必须需要让父进程读取到它的状态,回收子进程信息。只有这样,子进程才能得到“救赎”,“魂魄”才能归天

    (2)等待进程的方法

    A:wait方法

    这篇文章中讲过一个例子,通过那个例子我们看到了子进程死去之后变为了僵尸状态

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
       // printf("还没执行fork函数时的本进程为:%d\n",getpid());
        pid_t ret=fork();//其返回值类型是pid_t型的
        sleep(1);
        if(ret>0)//父进程返回的是子进程ID
        {
          while(1)
          {
            printf("----------------------------------------------------\n");
            printf("父进程一直在运行\n");
            sleep(1);
          }
        }
        else if(ret==0)//子进程fork返回是0
        {
          int count=0;
          while(count<=10)
          {
            printf("子进程已经运行了%d秒\n",count+=1);
            sleep(1);
          }
          exit(0);//让子进程运行10s
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    
    

    使用一个脚本动态查看

    while :; do ps axj | head -1 && ps axj | grep a.out | grep -v grep;sleep 1;echo "###########";done
    
    

    在这里插入图片描述
    修改代码如下,子进程在10s后退出,父进程在10s后继续运行,运行至第15s,跳出循环,加入语句wailt(NULL)以回收子进程

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
       // printf("还没执行fork函数时的本进程为:%d\n",getpid());
        pid_t ret=fork();//其返回值类型是pid_t型的
        sleep(1);
        if(ret>0)//父进程返回的是子进程ID
        {
    	  int count=1;
          while(count<=15)
          {
            printf("----------------------------------------------------\n");
            printf("父进程运行了%d秒\n",count);
    		count++;
            sleep(1);
          }
    	  wait(NULL);//回收子进程
        }
        else if(ret==0)//子进程fork返回是0
        {
          int count=1;
          while(count<=10)
          {
            printf("子进程已经运行了%d秒\n",count);
    		count++;
            sleep(1);
          }
          exit(0);//让子进程运行10s
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    
    

    如下可以发现,当父进程将子进程回收后,僵尸进程也消失了
    在这里插入图片描述
    如果父进程里只写上wait(NULL),那么就表示父进程阻塞在这里,等着子进程死亡,回收子进程

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
       // printf("还没执行fork函数时的本进程为:%d\n",getpid());
        pid_t ret=fork();//其返回值类型是pid_t型的
        sleep(1);
        if(ret>0)//父进程返回的是子进程ID
        {
    		printf("父进程正在等待子进程死亡\n");
    		wait(NULL);//进程阻塞
    		printf("子进程已经死亡,父进程退出\n");
    		exit(0);
    	}
        else if(ret==0)//子进程fork返回是0
        {
          int count=1;
          while(count<=10)
          {
            printf("子进程已经运行了%d秒\n",count);
    		count++;
            sleep(1);
          }
          exit(0);//让子进程运行10s
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    
    

    效果如下
    在这里插入图片描述

    B:waitpid方法

    1:基本使用

    这是waitpid的函数原型:pid_ t waitpid(pid_t pid, int *status, int options); 其中形参pid_t pid就是需要等待的子进程的pid(如果设置为-1,表示等待任意一个子进程,其实就等同于了wait),当等待成功时,函数的返回值就是子进程的PID

    如下,修改上面的代码,使用waitpid等待子进程(waitpid后面的两个参数先用NULL和0),然后判断waitpid的返回值和字进程PID是否相等,如果相等,则输出“等待成功”,否则输出“等待失败”。

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
       // printf("还没执行fork函数时的本进程为:%d\n",getpid());
        pid_t ret=fork();//其返回值类型是pid_t型的
        sleep(1);
        if(ret>0)//父进程返回的是子进程ID
        {
    		printf("父进程正在等待子进程死亡\n");
    		pid_t rec=waitpid(ret,NULL,0);//等待子进程,阻塞
    		if(rec==ret)//如果返回值是子进程id,等待成功
    		{
    			printf("等待成功\n");
    			exit(0);
    		}
    		else
    		{
    			printf("等待失败\n");
    			exit(0);
    		}
    			
    		
    	}
        else if(ret==0)//子进程fork返回是0
        {
          int count=1;
          while(count<=10)
          {
            printf("子进程[%d]已经运行了%d秒\n",getpid(),count);
    		count++;
            sleep(1);
          }
          exit(0);//让子进程运行10s
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    
    

    在这里插入图片描述
    2:重点理解第二个参数int* status

    首先大家可以发现这个参数比较特殊,需要注意的是这个参数wait也有,它是一个指针,是一个输出型参数。在外面定义一个变量,然后把这个变量的地址传给waitpid(如果传入NULL表示不关心子进程的退出状态信息),然后操作系统会根据这个变量的值,将子进程的退出信息反馈给父进程

    这个status在理解的时候稍微有点难度。对于一个进程来说,如果不要管它结果对不对,那么退出时只有两种状态:一种是正常退出(操作系统没有发送信号)另外一种就是异常退出(操作系统发送了终止信号)
    而这个status参数的目的就是为了判断你这个进程究竟是异常退出还是正常退出,如果是正常退出再看一下你的退出码是否是0,如果不是0,那么根据我业务的相关信息进行判断,报出相应的错误

    在32位环境中,int有4个字节,32个比特位,为了判断状态,只研究status的最后两个字节,也就是低16位。在低16位中,前高8位保存子进程的退出状态,低八位记录接受到的信号值
    在这里插入图片描述

    • 大家还记得kill 的参数列表吗,它的范围是1-64,你可以理解为不管是否正常退出操作系统都在发送信号,只不过正常时发送的是0,异常时发送的是非0
      在这里插入图片描述

    如下程序,子进程设置其退出码为exit(3)

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
        pid_t ret=fork();
        sleep(1);
        if(ret>0)
        {
    		printf("父进程正在等待子进程死亡\n");
    		int st;//st传地址进去
    		pid_t rec=waitpid(ret,&st,0);//阻塞
    		if(rec==ret)//如果返回值是子进程id,等待成功
    		{
    			printf("等待成功\n");
    			printf("st是%d\n",st);//查看st结果
    			exit(0);
    		}
    		else
    		{
    			printf("等待失败\n");
    			exit(0);
    		}
    			
    		
    	}
        else if(ret==0)//子进程fork返回是0
        {
          int count=1;
          while(count<=10)
          {
            printf("子进程[%d]已经运行了%d秒\n",getpid(),count);
    		count++;
            sleep(1);
          }
          exit(3);//让子进程运行10s
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    
    

    结果如下
    在这里插入图片描述
    其结果为768,输出的并不是3。我们可以这样想象, st用来判断:进程是否异常退出,如果正常退出结果又是否正确? 所以其传入时低16位默认就将其设为00000000 00000000,也就是默认正常。上面的例子中,并没有出现逻辑上的大问题,只是我的退出码是3,我想要说明这个进程是正常退出的只是结果有误。前面说过,高8位是子进程的退出状态,而3的二进制是11,将其“贴合”在高八位上,就变成了00000011 00000000,而其结果正好就是768。

    那么怎么输出真实的退出码呢,这其实就是C语言的知识了,只需把结果右移8,为了保证结果正确,再和0xff(11111111)进行与运算,也即是st>>8&0xff
    在这里插入图片描述
    现在我们故意再次犯一个“浮点异常”的错误,代码如下

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
        pid_t ret=fork();
        sleep(1);
        if(ret>0)
        {
    		printf("父进程正在等待子进程死亡\n");
    		int st;//st传地址进去
    		pid_t rec=waitpid(ret,&st,0);//阻塞
    		if(rec==ret)//如果返回值是子进程id,等待成功
    		{
    			printf("等待成功\n");
    			printf("st是%d\n",st);//查看st结果
    			exit(0);
    		}
    		else
    		{
    			printf("等待失败\n");
    			exit(0);
    		}
    			
    		
    	}
        else if(ret==0)//子进程fork返回是0
        {
          int count=1;
          while(count<=10)
          {
            printf("子进程[%d]已经运行了%d秒\n",getpid(),count);
    		count++;
            sleep(1);
          }
          int x=1/0;//浮点异常
          exit(0);
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    

    结果如下,其状态返回码是8,而之前咋们说过kill -8发送的就是浮点异常错误信息,而8的二进制是1000,也就是一旦出现异常,操作系统发送信号,将这个信号的二进制“贴合”到低8位上,也就是00000000 00001000,于因此退出码是8
    在这里插入图片描述
    所以如果进程不是异常退出的,那么操作系统就不可能给进程发送1-64的信号,其低8位就是0,然后子进程的退出码让其二进制形式“贴合”到高8位,那么要正常输出必须右移8位(虽然进程没有异常退出,但是我们需要获得退出码来验证这个进程的执行结果是否是正确的,依照不同的退出码返回不同的错误信息);如果进程是异常退出的,那么操作系统给进程发送kill信号,让其信号值的二进制“贴合”到低八位,已经异常退出了,所以高8位一定是0,所以输出退出码,不需要右移

    所以我们现在可以去判断进程是否是异常退出的,如果是正常退出,其退出码又是多少。如果是正常退出,那么其低8位一定是0,如果是异常退出其低8位一定不是0,所以我们让st和00000000 11111110(0x7f)进行与运算,如果其结果是0那么一定是正常退出的,反之不是0,那么一定是异常退出

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {;
        pid_t ret=fork();//其返回值类型是pid_t型的
        sleep(1);
        if(ret>0)//父进程返回的是子进程ID
        {
    		printf("父进程正在等待子进程死亡\n");
    		int st=0;
    		pid_t rec=waitpid(ret,&st,0);//阻塞
    		if(rec==ret)//如果返回值是子进程id,等待成功
    		{
    			printf("等待成功\n");
    			if((st&0x7f)==0)
    			{
    				printf("正常退出且其退出码为:%d\n",(st>>8)&0xff);
    			}
    			else
    			{
    				printf("异常退出,退出信号是:%d\n",st&0x7f);
    			}
    			
    		}
    		else
    		{
    			printf("等待失败\n");
    			exit(0);
    		}
    			
    		
    	}
        else if(ret==0)//子进程fork返回是0
        {
          int count=1;
          while(count<=10)
          {
            printf("子进程[%d]已经运行了%d秒\n",getpid(),count);
    		count++;
            sleep(1);
          }
    	  int x=1/0;
          exit(0);
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    
    
    • 下面进行测试,从上到下分别是正常退出,正常退出但结果不对和异常退出
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

    可以看出上面分析过程与代码十分复杂,但是咋们在实际开发时并不这样写,这个逻辑的函数已经封装好了,它是宏函数

    • WIFEXITED(status):判断进程是否异常退出,如果正常退出返回真
    • WEXITSTATUS(status):如果正常退出,看其结果是否正确

    所以代码可以改写如下

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
        pid_t ret=fork();//其返回值类型是pid_t型的
        sleep(1);
        if(ret>0)//父进程返回的是子进程ID
        {
    		printf("父进程正在等待子进程死亡\n");
    		int st=0;
    		pid_t rec=waitpid(ret,&st,0);//阻塞
    		if(rec==ret)//如果返回值是子进程id,等待成功
    		{
    			printf("等待成功\n");
    			if(WIFEXITED(st))//如果为真,正常退出
    			{
    				printf("正常退出且退出码为%d\n",WEXITSTATUS(st));
    			}
    			else
    			{
    				printf("异常退出,信号值为%d\n",st&0x7f);
    			}
    			
    			
    		}
    		else
    		{
    			printf("等待失败\n");
    			exit(0);
    		}
    			
    		
    	}
        else if(ret==0)//子进程fork返回是0
        {
          int count=1;
          while(count<=10)
          {
            printf("子进程[%d]已经运行了%d秒\n",getpid(),count);
    		count++;
            sleep(1);
          }
          exit(3);
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    
    

    在这里插入图片描述

    C:进程非阻塞式等待

    前面讲的都是进程的阻塞式等待,也就是父进程遇到wai/waitpid时,父进程就卡在那里,然后静静得看着子进程,子进程不死那么父进程就不向后执行

    进程的非阻塞式等待和waitpid的第三个参数options有关,如果options设置为WNOHANG,表示:如果指定的子进程还没有结束那么就不等待了,返回0,如果结束了就返回该子进程的ID

    下面这段程序中的do while循环表示每次用rec接受waitpid的返回值,由于waitpid设置了WNOHANG,因此如果要等待的子进程还没有结束它就会返回0。一直让这个循环走下去,直到子进程结束,其返回值是子进程的PID,也就是大于0的数,然后循环终止

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
        pid_t ret=fork();//其返回值类型是pid_t型的
        sleep(1);
        if(ret>0)//父进程返回的是子进程ID
        {
    		printf("父进程正在等待子进程死亡\n");
    		int st=0;
    		pid_t rec=0;
    		
    		do
    		{
    			rec=waitpid(ret,&st,WNOHANG);//如果子进程没有结束返回0
    			if(rec==0)//判断是否结束,
    			{
    				printf("子进程还在运行当中,请稍后再来\n");
    			}
    			else
    			{
    				break;//如果返回的是子进程的PID,那么就跳出循环
    			}
    			sleep(1);
    			
    		}while(1);//直到waitpid返回不是0,也就是子进程结束了
    		
    		
    		if(rec==ret)//如果返回值是子进程id,等待成功
    		{
    			printf("等待成功\n");
    			if(WIFEXITED(st))//如果为真,正常退出
    			{
    				printf("正常退出且退出码为%d\n",WEXITSTATUS(st));
    			}
    			else
    			{
    				printf("异常退出,信号值为%d\n",st&0x7f);
    			}
    			
    			
    		}
    		else
    		{
    			printf("等待失败\n");
    			exit(0);
    		}
    			
    		
    	}
        else if(ret==0)//子进程fork返回是0
        {
          int count=1;
          while(count<=10)
          {
            printf("子进程[%d]已经运行了%d秒\n",getpid(),count);
    		count++;
            sleep(1);
          }
    
          exit(3);
        }
        else
          printf("进程创建失败\n");
    
        sleep(1);
        return 0;
    }
    
    
    

    效果如下
    在这里插入图片描述
    阻塞和非阻塞区别:想象你在电脑上下载游戏,阻塞就是从游戏下载开始我就一直盯着屏幕,它不下载完我就不干别的事情,而非阻塞就是游戏一直下载,我时不时的打开下载器看看进度,没有下载好的话我下次在看,但是在空余时间我可以看看视频,打打字

    展开全文
  • 浅谈进程和线程

    2021-02-25 11:24:20
    俗称轻量级进程是进程中一条执行路径,cpu的基本调度单位,一个进程由一个多线程组成,每个线程完成不同的工作 多线程实际上宏观上并行,微观上串行 举个例子: JVM一个进程,当中默认包含主线程main,可...

    进程和线程

    什么是进程?

    正在运行的程序,操作系统进行资源分配的基本单位

    操作系统目前通过PID(process id)对多个进程进行区分
    在这里插入图片描述

    什么是线程?

    俗称轻量级进程,是进程中一条执行路径,是cpu的基本调度单位,一个进程由一个多线程组成,每个线程完成不同的工作

    多线程实际上是宏观上并行,微观上串行

    在这里插入图片描述

    举个例子:
    JVM是一个进程,当中默认包含主线程main,可通过代码创建多个独立线程,与main并发执行

    线程的组成

    • cpu时间片
    • 运行数据
      1. 堆空间:存储线程要用的对象,多个线程可以共享堆中的对象
      2. 栈空间:存储线程要用的局部变量,每个线程有独立的栈

    线程状态

    线程5种状态

    线程常用方法

    1. 更改线程优先级
    setPriority(int newPriority)
    
    1. 当前执行线程体休眠指定时间(毫秒数)
      存在异常InterruptedException
      休眠时间结束后,线程进入就绪状态

    每个对象都有一把锁,sleep不会释放这把锁

    static void sleep(long millis)
    
    1. 插队
    void join()
    
    1. 暂停正在执行的线程对象,并执行其他线程
    static void yield()
    
    1. 中断线程(建议不要使用这种方法使线程停止)
    void interrupt()
    

    建议让线程正常停止,不要死循环,或者使用一个标志位进行终止变量,从而使线程停止

    public class Demo implements Runnable {
        private boolean flag = true;
    
        @Override
        public void run() {
            int i = 0;
            while(flag){
                System.out.println("thread"+i++);
            }
        }
    
        public void stop(){
            this.flag = false;
        }
        public static void main(String[] args) {
            Demo demo = new Demo();
            new Thread(demo).start();
    
            for (int i = 0; i < 1000000; i++) {
                if(i==999999) {
                    demo.stop();
                    System.out.println("stop it...");
                }
            }
        }
    }
    

    在这里插入图片描述

    1. 测试线程是否存活
    boolean isAlive()
    

    两者区别

    进程是操作系统进行资源分配的基本单位

    线程是cpu的基本调度单位

    程序运行后至少有一个进程,而一个进程至少需要一个线程

    同进程的线程间可以共享数据段地址,进程间不能共享

    展开全文
  • 线程库在管理线程时,要为用户线程所属的进程创建专用的线程表(存在于用户空间,不在进程的pcb中)。利用线程表中记录的每个线程的程序计数器、堆栈指针,寄存器和状态等,可以完成在用户线程的切换(包括线程的...

    参考一: link.
    参考二: link.
    参考三: 内核堆栈与用户堆栈.
    一、用户级线程
    什么是用户线程:
    用户线程由线程库管理,对内核是透明的。不支持线程(即不支持内核线程)的操作系统上只能用这种方式实现用户线程。
    管理用户线程的方法:
    线程库在管理线程时,要为用户线程所属的进程创建专用的线程表(存在于用户空间,不在进程的pcb中)。利用线程表中记录的每个线程的程序计数器、堆栈指针,寄存器和状态等,可以完成在用户线程的切换(包括线程的现场保护)。并可以制定属于进程的用户线程调度算法(局限性很大,因为没有计时器,某一线程可能会“永久”运行,其它线性得不到运行)。
    用户级线程的阻塞:
    如果线程做了引起本地阻塞(我理解是线程库中定义的阻塞)的事情,就会调用同一进程中其它就绪的线程。
    如果线程进行了阻塞系统调用,则该进程会被阻塞,进程中的其它线程不能执行。如果线程进行的是非阻塞系统调用,则该线程继续执行,如果不能继续执行(多半是触发了本地阻塞)就执行同一进程中的其它线程。
    用户线程优点缺点:
    不需要陷入内核,所以切换快。但实现不了用户线程的并行。
    二、内核线程
    什么是内核线程:
    操作系统支持和管理线程时才能有内核线程。内核线程的创建,调度,撤销要由内核来完成。内核线程的线程表(thread table)位于内核中,包括了线程控制块(TCB)(上面的用户线程线程表存在用户空间)。内核线程们公用一个地址空间(3~4G),即内核线程1的逻辑地址空间若和内核线程2的逻辑地址空间使用相同的地址,映射到物理地址也是相同的。内核线程是“独立运行在内核空间的标准进程"
    内核线程的种类:
    1、线程按周期性间隔运行,检测特定资源的使用,在用量超出或者低于预置的限制时采取行动,这种线程往往负责守护。
    2、在线程启动后则一直等待,直到内核线程请求执行某一特定的操作。
    3、用来支持LWP,完成不同的调度模型。(见下文)
    三、系统调用:
    中断处理、进程切换都跟内核线程是不同的概念,中断管理与进程切换都属于内核代码段部分(0~3G),但却不是内核线程。
    执行系统调用时还是那个进程,但由用户堆栈切换到了内核堆栈。
    四、linux的轻量级线程LWP
    轻量级线程(LWP)是一种由内核支持的用户线程。它是基于内核线程的高级抽象,因此只有先支持内核线程,才能有LWP。每一个进程有一个或多个LWPs,每个LWP由一个内核线程支持。由于每个LWP都与一个特定的内核线程关联,因此每个LWP都是一个独立的线程调度单元。即使有一个LWP在系统调用中阻塞,其它LWP也可以被调度,不会影响整个进程的执行。
    LWP上如果只有一个”用户线程“,这两个的组合也叫用户线程,这个用户线程是可以被单独调度的,与上文用户线程不同。这是一对一调度模型
    线程库可以在每个LWP上都创建”用户级线程“,如果只有一个LWP就是多对一调度模型。如果有多个LWP就是多对对调度模型。
    五、协程
    由上文可知,用户级线程切换代价(同一个LWP中的用户线程)<内核线程切换代价(包括LWP之间的切换)<进程切换代价。
    但用户线程切换也是很慢的,用户线程切换慢的原因是每个都有自己的程序计数器、堆栈指针,寄存器和状态等东西。
    协程就没有这些东西,所以协程切换起来很快,一个线程如果拥有多个协程,那并发时就不用付出切换的代价。关于协程库我了解并不多。
    协程也是对内核不可见的。

    展开全文
  • 例如:迅雷是一个进程,当中的多个下载任务即是多个线程二、线程和进程的区别进程是操作系统资源分配的基本单位,线程是cpu的基本调度单位。一个程序运行后至少有一个进程。一个进程可以包含多个线程,但是至少需要...

    一、什么是线程

    线程,又称轻量级进程。

    程序中的一个顺序控制流程,同时也是cpu的基本调度单位。

    进程由多个线程组成,彼此间完成完成不同的工作,交替执行,称为多线程。

    例如:迅雷是一个进程,当中的多个下载任务即是多个线程

    二、线程和进程的区别

    进程是操作系统资源分配的基本单位,线程是cpu的基本调度单位。

    一个程序运行后至少有一个进程。

    一个进程可以包含多个线程,但是至少需要一个线程

    进程间不能共享数据段地址,但同进程的线程之间可以

    三、线程的组成及特点

    任何一个线程都具有基本的组成部分:

    1.cpu时间片:操作系统会为每个线程分配执行时间。

    2.运行数据:

    堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象

    栈空间:存储线程需使用的局部编码,每个线程都拥有独立的栈

    3.线程的逻辑代码

    线程的特点:

    1.线程抢占式执行:效率高;可以防止单一线程长时间独占cpu

    2.在单核cpu中,宏观上同时执行,微观上顺序执行

    四、创建线程的方式

    1、继承Thread类 重写run()方法

    获取线程名称:第一种方法:用this.getid() 和  this.getName() 获取线程的ID和名称

    第二种方法:用this.currentThread() 获取当前线程

    线程.setName();给线程名称赋值

    注意:线程的启动 用 start() 方法

    线程的名称可以修改 线程的ID不可修改

    packagecom.monv.Thread;/*** 创建线程类 继承 Thread

    *@authorAdministrator

    **/

    public class MyThread extendsThread{publicMyThread() {//TODO Auto-generated constructor stub

    }publicMyThread(String name){super(name);

    }

    @Overridepublic voidrun() {for(int i = 0;i<100;i++){//1.第一种方法 用this.getid() 和 this.getName() 获取线程的ID和名称//System.out.println("线程ID:"+this.getId()+"线程名称:"+this.getName()+"子线程执行-------"+i);//2.第二种方法 用this.currentThread() 获取当前线程

    System.out.println("线程ID:"+this.currentThread().getId()+" 线程名称:"+this.currentThread().getName()+" 子线程执行------"+i);

    }

    }

    }---------------------------------------------------------

    packagecom.monv.Thread;/*** 测试线程 main是主线程

    *@authorAdministrator

    **/

    public classTestThread {public static voidmain(String[] args) {//1.创建线程

    MyThread testMyThread = new MyThread("这是第一个子线程");//2.启动线程,用Start,不能用Run//修改线程的名称 要写在线程执行前//testMyThread.setName("这是第一个子线程");

    testMyThread.start();

    MyThread testMyThread2= new MyThread("这是第二个子线程");//2.启动线程,用Start,不能用Run//修改线程的名称 要写在线程执行前//testMyThread2.setName("这是第二个子线程");

    testMyThread2.start();//主线程执行

    for (int i = 0; i <100; i++) {

    System.out.println("主线程执行=============="+i);

    }

    }

    }

    2.实现Runnable接口 重写run()方法

    注意:Runable也可以用内部类的方法实现

    packagecom.monv.Thread;public class MyRunnable implementsRunnable{

    @Overridepublic voidrun() {for (int i = 0; i<100 ;i++){

    System.out.println(Thread.currentThread().getName()+"++++++++++"+i);

    }

    }

    }------------------------------------------------------

    packagecom.monv.Thread;public classTestRunnable {public static voidmain(String[] args) {//1.创建MyRunnable对象,表示线程要执行的功能

    MyRunnable runnable = newMyRunnable();//2.创建线程

    Thread thread = new Thread(runnable, "我的线程");//3.执行

    thread.start();for (int i = 0; i < 100;i++){

    System.out.println("main------------"+i);

    }

    }

    }-------------------------------------------------

    -------------匿名内部类方法实现---------------------

    packagecom.monv.Thread;public classTestRunnable {public static voidmain(String[] args) {//创建匿名内部类

    Runnable runnable = newRunnable() {

    @Overridepublic voidrun() {for (int i = 0; i < 100;i++){

    System.out.println(Thread.currentThread().getName()+"++++++++++++"+i);

    }

    }

    };

    Thread thread= new Thread(runnable, "我的线程");

    thread.start();for (int i = 0; i < 100;i++){

    System.out.println("main------------"+i);

    }

    }

    }

    五、线程的状态

    7bbdf4511ac96d62a3d86d88e84429e9.png

    六、常见的方法

    1.休眠:public static void sleep(long millis); 当前线程主动休眠millis毫秒

    2.放弃:public static void yield(); 当前主线程主动放弃时间片,回到就绪状态,竞争下一次时间片

    3.加入:public final void join();允许其他线程加入到当前线程中

    --------1.sleep-----------

    packagecom.monv.Thread;public class SleepThread extendsThread{

    @Overridepublic voidrun() {for (int i =0 ;i<10 ;i++){

    System.out.println(Thread.currentThread().getName()+"............."+i);try{

    Thread.sleep(1000);

    }catch(InterruptedException e) {//TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    }

    }

    ---------测试-------------packagecom.monv.Thread;public classTestSleep {public static voidmain(String[] args) {

    SleepThread s1= newSleepThread();

    s1.start();

    SleepThread s2= newSleepThread();

    s2.start();

    }

    }--------2.yield-----------

    packagecom.monv.Thread;public class YieldThread extendsThread{

    @Overridepublic voidrun() {for(int i =0;i< 10;i++){

    System.out.println(Thread.currentThread().getName()+"+++++++++++"+i);//主动放弃cpu

    Thread.yield();

    }

    }

    }

    ---------测试------------------packagecom.monv.Thread;public classTestYield {public static voidmain(String[] args) {

    YieldThread y1= newYieldThread();

    YieldThread y2= newYieldThread();

    y1.start();

    y2.start();

    }

    }--------3.join-----------

    packagecom.monv.Thread;public class JoinThread extendsThread{

    @Overridepublic voidrun() {for (int i = 0; i < 30; i++) {

    System.out.println(Thread.currentThread().getName()+"........."+i);try{

    Thread.sleep(500);

    }catch(InterruptedException e) {//TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    }

    }

    ----------测试---------------packagecom.monv.Thread;public classTestJoin {public static voidmain(String[] args) {

    JoinThread jo1= newJoinThread();

    jo1.start();try{

    jo1.join();//把j1加入当前线程(主线程main),并阻塞当前线程,直到加入线程执行完毕当前线程才会继续执行

    } catch(InterruptedException e1) {//TODO Auto-generated catch block

    e1.printStackTrace();

    }//主线程

    for (int i =0;i<20;i++){

    System.out.println(Thread.currentThread().getName()+"......"+i);try{

    Thread.sleep(300);

    }catch(InterruptedException e) {//TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    }

    }

    七、线程等待

    c7c5ab784c686e189a2f3b19f5677343.png

    展开全文
  • 我们知道进程是一个可执行程序的运行实例,说白了就是运行着的程序,每一个运行着的程序都可以看做是一个独立的进程,会占用系统资源,在内存中有对应的代码空间和数据空间,操作系统调度运行,创建,分配系统资源...
  • 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位 一个程序运行后至少有一个进程 一个进程可以包含多个线程,但是至少需要有一个进程 进程间不能共享数据段地址,但同进程的线程之间可以 线程的组成 .....
  • 进程完成一个请求,父进程把请求数减1;当父进程发现请求数 >= 子进程数时,父进程创建新的子进程,并把子进程数加1(当然子进程数有个预先上限);当父进程发现子进程数大于请求数加1时,父进程杀死多余的子...
  • Liunx之线程

    2018-06-14 10:59:26
    多任务可以多进程完成,也可以一个进程内的多线程完成。 线程和进程的对比 进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它 分配资源,然后把该...
  • Java--多线程学习

    2020-07-21 11:04:52
    多线程什么是进程什么是线程进程和线程的区别线程的组成线程的特点创建线程继承Thread获取线程的名称修改线程的名称买票模拟实现Runnable接口 什么是进程 正在运行的程序,系统进行资源分配的基本单位。 目前操作...
  • 什么是线程? 线程是调度CPU的最小单元,...在多处理器系统上,多线程在多处理器上并行运行,线程的创建调度和管理内核完成,效率比用户级线程要慢,比进程操作快。 什么是线程池? 线程池就是创建若干个可执行的
  • 内核支持线程和用户级线程

    千次阅读 2017-12-21 09:54:59
     对于通常的进程,无论系统进程还是用户进程进程创建、撤销,以及要求系统设备完成的i/o操作,都利用系统调用而进入内核,再内核中的相应处理程予以完成的。进程的切换同样在内核的支持下实现的。...
  • 操作系统复习题

    千次阅读 2019-12-19 16:22:13
    (1)创建到就绪:处于创建状态的进程,当其获得所需的资源以及对其PCB的初始化工作完成后,便由创建状态转入就绪状态。 (2)就绪到执行:处于就绪状态的进程,在调度程序为之分配了处理器之后,该进程就进入执行...
  • JavaSE学习之线程初学

    2020-05-11 00:16:06
    进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程。 2.JVM虚拟机一个进程,当中默认包含主线程(Main),可通过代码创建多个独立线程,与Main并发执行。 2、线程的组成 3、线程的状态 4、线
  • 1. 协程介绍 1.1 什么是协程 协程(Coroutine)也叫用户态线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以... 内核态的线程是由操作系统来进行调度的,在切换线程上下文时,要先...
  • 什么是协程 协程(Coroutine)也叫用户态线程,其通过协作而不是抢占来进行切换。... 内核态的线程是由操作系统来进行调度的,在切换线程上下文时,要先保存上一个线程的上下文,然后执行下一个线程,当...
  • 浅谈微内核

    千次阅读 多人点赞 2018-09-23 18:32:53
    像Linux就是典型的宏内核,它除了时钟中断、进程创建与销毁、进程调度进程间通信外,其他的文件系统、内存管理、输入输出、设备驱动管理都需要内核完成。微内核,又称为微核心,一种内核的设计架构,一群数量...
  • 初识java线程池

    2020-10-22 23:40:14
    1.用户线程和内核线程 用户线程(ULT): 用户程序实现,不依赖操作系统核心,应用提供创建...线程的创建调度、和管理内核完成,效率 要比ULT慢,比进程操作快 2.JVM使用的是什么线程模型? java使用的KLT线程模
  • java学习多线程处理

    2013-06-28 08:09:39
    线程的调度管理是由进程完成的。 注意:编程时,必须确保线程不会妨碍同一进程的其他线程 线程的分类:系统级线程(又称为核心级线程,负责管理调度不同进程之间的多个线程,由操作系统直接管理) 用户级线程...
  • 一个程序作为一个进程运行,程序运行过程中可能会创建多个线程。一个线程在一个时刻只能运行在一个CPU核心上。 为什么使用多线程? ①异步执行 ②利用多CPU资源实现真正意义上的并列执行 线程的应用场景: ①...
  • 2.1.5 堆栈溢出一般是由什么原因导致的? 2.1.6 什么函数不能声明为虚函数? 2.1.7 冒泡排序算法的时间复杂度是什么? 2.1.8 写出float x 与“零值”比较的if语句 2.1.9 Internet采用哪种网络协议?该协议的主要...
  • 6.堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源。 7.什么函数不能声明为虚函数? constructor函数不能声明为虚函数。 8.冒泡排序算法的时间复杂度是什么? 时间复杂度是O(n^2)。 9.写出float x 与“零...
  • 答:启动一个线程调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 40.接口是否可...
  • Unix/Linux 编程实践教程.PDF

    千次下载 热门讨论 2010-09-03 18:34:12
    1.2 什么是系统编程 1.2.1 简单的程序模型 1.2.2 系统模型 1.2.3 操作系统的职责 1.2.4 为程序提供服务 1.3 理解系统编程 1.3.1 系统资源 1.3.2 目标:理解系统编程 1.3.3 方法:通过三个问题来理解 1.4 ...
  • 实务5 Oracle安装完成后,用户sys与system默认的密码是什么 实务 实务6 如何找到Oracle安装时生成的日志文件 实务7 安装Oracle时,是否需要安装JDK 实务8 解压安装文件时,报告无权限 实务 实务9 安装Oracle软件并...
  • 实务5 oracle安装完成后,用户sys与system默认的密码是什么 实务 实务6 如何找到oracle安装时生成的日志文件 实务7 安装oracle时,是否需要安装jdk 实务8 解压安装文件时,报告无权限 实务 实务9 安装oracle软件并...
  • 实务5 oracle安装完成后,用户sys与system默认的密码是什么 实务 实务6 如何找到oracle安装时生成的日志文件 实务7 安装oracle时,是否需要安装jdk 实务8 解压安装文件时,报告无权限 实务 实务9 安装oracle软件并...
  • 实务5 oracle安装完成后,用户sys与system默认的密码是什么 实务 实务6 如何找到oracle安装时生成的日志文件 实务7 安装oracle时,是否需要安装jdk 实务8 解压安装文件时,报告无权限 实务 实务9 安装oracle软件并...
  • 实务5 oracle安装完成后,用户sys与system默认的密码是什么 实务 实务6 如何找到oracle安装时生成的日志文件 实务7 安装oracle时,是否需要安装jdk 实务8 解压安装文件时,报告无权限 实务 实务9 安装oracle软件并...

空空如也

空空如也

1 2 3 4
收藏数 79
精华内容 31
关键字:

创建进程是由什么调度完成