zoukankan      html  css  js  c++  java
  • 守护线程、守护进程

    1. 几点认识:

    1. java中有两类线程:user thread(用户线程),daemon thread(守护线程)
    2. 守护线程为其他线程的运行提供服务,例如GC线程(垃圾回收线程),内存管理线程。
    3. 虚拟机判断程序执行结束的标准时不考虑守护线程:如果user thread全部撤离,daemon thread因为无服务对象,所以虚拟机也就退出了。
    4. public final void setDaemon(boolean on) :用户自行设定守护线程
    5. 是JVM模拟了操作系统中的“守护进程”而定义出的一种机制。但与守护进程有所不同
      1. 不能将正在运行的线程设置为守护线程:thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。(守护进程是创建后,让其脱离会话+进程组+控制终端控制)
      2. 在Daemon中创建的线程也是守护线程。(守护进程fork()出的进程不是守护进程,因为其父进程不再是init进程)
      3. 不是所有的应用都可以分配给守护线程来服务。比如读写操作或计算逻辑,因为在daemon还没操作时,虚拟机已经退出了。

      例子:

    public class DaemonThreadTest {
        public static void main(String[] args){
            Thread t1 = new MyThread();
            Thread t2 = new Thread( new MyDaemon() );
            t2.setDaemon(true);
            t1.start();
            t2.start();
        }
    }
    
    class MyDaemon implements Runnable{
        public void run(){
            for( long i=0; i<9999999L; i++ )
                System.out.println("后台线程第" + i + "次执行!");
            try{
                Thread.sleep(7);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    
    class MyThread extends Thread{
        public void run(){
            for( int i=0; i<5; i++ )
                System.out.println("线程第" + i + "次执行!");
            try{
                Thread.sleep( 7 );
            }catch( InterruptedException e ){
                e.printStackTrace();
            }            
        }
    }

    输出:

    后台线程第0次执行!
    线程第0次执行!
    后台线程第1次执行!
    线程第1次执行!
    后台线程第2次执行!
    线程第2次执行!
    后台线程第3次执行!
    线程第3次执行!
    后台线程第4次执行!
    ...
    后台线程第339次执行!

    即前台线程保证执行完毕,后台线程不一定。

    2. 守护进程

    1. 定义:Linux或Unix操作系统中,在系统引导时会开启很多服务,这些服务就是守护进程,即后台服务进程。例如amd(自动安装NFS守候进程)、Lpd(打印服务器)等。
    2. 生存期长。通常在系统引导装入是启动,在系统关闭时终止。
    3. 独立于控制终端并且周期性地执行某种任务。从而其在执行过程中的信息不会在终端显示,并且也不会终端进程信息所打断。
      • 终端:Linux中,每个系统与用户交流的界面称为终端(terminal),每个从此终端开始运行的进程都会依附于此终端,该终端就称为这些进程的控制终端。
      • 当控制终端被关闭时,相应的用户进程都会自动关闭。而守护进程能突破这种限制,即它在系统关闭使才会终止。
    4. 什么时候使用守护进程?
      • 如果不想让某个进程因为用户/终端/其他变化而受到影响,则将该进程设为守护进程。
    5. 创建过程:
      1. 创建子进程,父进程退出。
        • 即创建子进程后,显示退出父进程,造成在终端这一进程已运行完毕的假象。之后的操作都由子进程完成。形式上做到与控制终端脱离。
        • 孤儿进程:父进程先于子进程退出,则称为孤儿进程。系统发现一个孤儿进程后,就自动有1号进程(init进程)收养,即该子进程称为init进程的子进程。
      2. 在子进程中创建新会话。
        • 继承:调用fork()函数,子进程会拷贝父进程的所有会话期、进程组、控制终端等。需要重设这些,才使子进程真正的与控制终端脱离
        • 进程组:一个或多个进程集合。每个进程有进程pid,进程组有组ID。pid和进程组ID都是一个进程的必备属性。每个进程组都有组长进程,其进程号等于进程组ID。当进程组ID不受组长进程退出的影响。
        • 会话期:一个或多个进程组集合。通常,一个会话始于用户登录,终于用户退出,这之间的所有进程都属于该会话期。
        • setsid:创建新会话,并担任该会话组组长。有3个作用:
          • 让进程摆脱原会话的控制
          • 让进程摆脱原会话组的控制
          • 让进程摆脱原控制终端的控制
      3. 改变当前目录为根目录
        • 继承:fork()创建的子进程还拷贝了父进程的当前工作目录。需要重设。
        • 进程运行中,当前目录所在文件系统是不能卸载的,即原工作目录无法卸载。可能造成很多麻烦,如需要进入单用户模式。所以必须重设当前目录。
        • chdir("/"):重设为根目录
      4. 重设文件权限掩码
        • 文件权限掩码:屏蔽掉文件权限中的对应位。有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。
        • 继承:fork()创建的子进程还继承了父进程的文件权限掩码。
        • umask(0):重设为0,灵活性更强。
      5. 关闭文件描述符
        • 继承:fork()创建的子进程从父进程继承了一些已经打开了的文件。被打开的进程可能永远不会被守护进程使用,却消耗资源。所以必须手动关闭文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)。
      6. 守护进程退出处理
        • 可能需要支持用户在外部手动停止守护进程运行,通常使用kill命令。编码实现kill发出的signal信号处理,达到线程正常退出。
    signal(SIGTERM, sigterm_handler);
    void sigterm_handler(int arg)
    {
    _running = 0;
    }
    

      创建守护进程的一个实例:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #define MAXFILE 65535
    void sigterm_handler(int arg);
    volatile sig_atomic_t _running = 1;
    int main()
    {
    pid_t pc,pid;
    int i,fd,len,flag = 1;
    char *buf="this is a Dameon
    ";
    len = strlen(buf);
    pc = fork(); //第一步
    if(pc<0){
    printf("error fork
    ");
    exit(1);
    }
    else if(pc>0)
    exit(0);
    pid = setsid(); //第二步[1] 
    if (pid < 0)
    perror("setsid error");
    chdir("/"); //第三步
    umask(0); //第四步
    for(i=0;i<MAXFILE;i++) //第五步
    close(i);
    signal(SIGTERM, sigterm_handler);
    while( _running )
    {
    if( flag ==1 &&(fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0)
    {
    perror("open");
    flag=0;
    exit(1);
    }
    write(fd,buf,len);
    close(fd);
    usleep(10*1000); //10毫秒
    }
    }
    void sigterm_handler(int arg)
    {
    _running = 0;
    }
    createDeamon
  • 相关阅读:
    ElasticSearch(二) Transport Client Connection By Domain
    ElasticSearch(一) Transport Client
    如何把Spring Boot打包成war
    Lucene Query种类
    JAVA Http Basic auth
    Java 多线程系列2——多线程的生命周期及生产消费者模型
    Java 多线程系列1——浅聊JAVA 线程池的一般用法
    JS 实现右下角弹窗
    JS 实现兼容IE图片向左或向右翻转
    Vue Input输入框两侧加减框内数字组件
  • 原文地址:https://www.cnblogs.com/hf-cherish/p/4645890.html
Copyright © 2011-2022 走看看