资讯专栏INFORMATION COLUMN

一起学并发编程 - 守护线程

junnplus / 3280人阅读

摘要:的作用是为其他线程的运行提供服务,比如说线程。在某些平台上,指定一个较高的参数值可能使线程在抛出之前达到较大的递归深度。参数的值与最大递归深度和并发程度之间的关系细节与平台有关。

今天研究了下Java线程基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程),以及构造器中的stackSize.....

守护线程

估计学过Unix开发但是没有细致学习Java的同学们会疑惑了,操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java语言机制是构建在JVM的基础之上的,意思是Java平台把操作系统的底层给屏蔽起来,所以它可以在它自己的虚拟的平台里面构造出对自己有利的机制,而语言或者说平台的设计者多多少少是受到Unix思想的影响,而守护线程机制又是对JVM这样的平台凑合,于是守护线程应运而生。

Daemon的作用是为其他线程的运行提供服务,比如说GC线程。其实User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开:如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以虚拟机也就退出了。

守护线程并非虚拟机内部可以提供,用户也可以自行的设定守护线程,方法:setDaemon(boolean on) 但是有几点需要注意:

thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常,因为你不能把正在运行的常规线程设置为守护线程。(备注:这点与守护进程有着明显的区别,守护进程是创建后,让进程摆脱原会话的控制+让进程摆脱原进程组的控制+让进程摆脱原控制终端的控制;所以说寄托于虚拟机的语言机制跟系统级语言有着本质上面的区别)

public final void setDaemon(boolean on) {
    checkAccess();
    if (isAlive()) {//如果处于运行状态,抛出异常
        throw new IllegalThreadStateException();
    }
    daemon = on;
}

Daemon线程中产生的新线程也是Daemon的。(这一点又是有着本质的区别了:守护进程fork()出来的子进程不再是守护进程,尽管它把父进程的进程相关信息复制过去了,但是子进程的进程的父进程不是init进程,所谓的守护进程本质上说就是父进程挂掉,init收养

不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为在Daemon Thread还没来的及进行操作时,虚拟机可能已经退出了。

Thread thread = new Thread(() -> {
    Thread innerThread = new Thread(() -> {
        try {
            for (int i = 1; i < 10; i++) {
                Thread.sleep(1_000);
                System.out.println("守护线程 " + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    innerThread.setDaemon(true);
    innerThread.start();
    try {
        for (int i = 1; i < 6; i++) {
            Thread.sleep(1_000);
            System.out.println("常规线程 " + i);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
thread.start();
日志分析
常规线程 1
守护线程 1
守护线程 2
常规线程 2
守护线程 3
常规线程 3
守护线程 4
常规线程 4
守护线程 5
常规线程 5

从上面的日志中可以看到,如果将innerThread设置成守护模式,那么待当前主线程完成处理退出后,守护线程也会随着销毁

JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台线程候一定要注意这个问题。

那么daemon Thread实际应用在那里呢?举个例子,Web服务器中的Servlet,容器启动时后台初始化一个服务线程,即调度线程,负责处理http请求,然后每个请求过来调度线程线程池中取出一个工作者线程来处理该请求,从而实现并发控制的目的。

线程函数

在学习Thread的时候,常见写法就是RunnableThreadName,所以这里重点讲解ThreadGroup以及stackSize的作用

public Thread(); 
public Thread(Runnable target); 
public Thread(String name); 
public Thread(Runnable target, String name); 
public Thread(ThreadGroup group, Runnable target); 
public Thread(ThreadGroup group, String name); 
public Thread(ThreadGroup group, Runnable target, String name); 
public Thread(ThreadGroup group, Runnable target, String name, long stackSize); 

第一种是实例化一个无参构造函数,ThreadNameGroupName为系统默认

第二种是实现了Runnable接口的类的实例,Thread类也实现了Runnable接口,因此,从Thread类继承的类的实例也可以作为target传入这个构造方法。

第三种是可以自定义ThreadName

第五种是可以指定该线程属于哪个ThreadGroup(线程组)

第八种是可以指定堆栈大小(比如压栈大小),这个值一般是CPU页面的整数倍,如x86的页面大小是4KB.在x86平台下,默认的线程栈大小是12KB.

ThreadName,可以通过创建Thread实例后调用,setName方法设置。默认线程名:Thread-N,N是线程建立的顺序,是一个不重复的正整数,它的来源基于Thread.nextThreadNum()

/* For autonumbering anonymous threads. */
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}

stackSize是一种具有平台依赖性的参数,它能指定堆栈的大小。 在某些平台上,指定一个较高的 stackSize 参数值可能使线程在抛出 StackOverflowError 之前达到较大的递归深度。stackSize 参数的值与最大递归深度和并发程度之间的关系细节与平台有关。在某些平台上,stackSize 参数的值无论如何不会起任何作用。作为建议,可以让虚拟机自由处理 stackSize 参数。

ThreadGroup group = new ThreadGroup("battcn-group");
Thread t1 = new Thread(() -> System.out.println("hello my name"s" + Thread.currentThread().getName() + " group name"s" + Thread.currentThread().getThreadGroup().getName()));
Thread t2 = new Thread(() -> System.out.println("hello my name"s" + Thread.currentThread().getName() + " group name"s" + Thread.currentThread().getThreadGroup().getName()), "thread-battcn2");
Thread t3 = new Thread(() -> System.out.println("hello my name"s" + Thread.currentThread().getName() + " group name"s" + Thread.currentThread().getThreadGroup().getName()), "thread-battcn3");
t1.start();
t2.start();
t3.start();
group.enumerate(new Thread[]{t2, t3});

以下是上部代码片段的日志输出,可以看到指定ThreadNameThreadGroup以及不指定的的区别

hello my name"s Thread-0  group name"smain
hello my name"s thread-battcn2  group name"smain
hello my name"s thread-battcn3  group name"smain

警告 如果对stackSize 有兴趣的可以试试下面代码,不过慎重,有可能吧你电脑内存跑完....

for (int i = 0; i < Integer.MAX_VALUE; i++) {
    new Thread(group, new Runnable() {
        @Override
        public void run() {
            try {
                add(1);
            } catch (Error error) {
                System.out.println(count);
                error.printStackTrace();
            }
        }
        private void add(int i) {
            count++;
            add(i + 1);
        }
    }, "thread-battcn-4",1 << 24).start();
}
- 说点什么

微信公众号:battcn(欢迎调戏)

全文代码:https://git.oschina.net/battcn/battcn-concurent

个人QQ:1837307557

battcn开源群(适合新手):3916196

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70510.html

相关文章

  • 一起并发编程 - 优雅关闭

    摘要:文本将介绍两种可以优雅的终止线程的方式第一种在多线程模式中有一种叫两步终止的模式可以优雅的终止线程,这种模式采用了两个步骤来终止线程,所以叫两步终止模式。 Java中原来在Thread中提供了stop()方法来终止线程,但这个方法是不安全的,所以一般不建议使用。文本将介绍两种可以优雅的终止线程的方式... 第一种 在JAVA《Java多线程模式》中有一种叫Two-Phase Term...

    曹金海 评论0 收藏0
  • Thread源码剖析

    摘要:现在已经没有强制线程终止的方法了由于方法可以让一个线程终止掉另一个线程被终止的线程会立即释放锁,这可能会让对象处于不一致的状态。 前言 昨天已经写了: 多线程三分钟就可以入个门了! 如果没看的同学建议先去阅读一遍哦~ 在写文章之前通读了一遍《Java 核心技术 卷一》的并发章节和《Java并发编程实战》前面的部分,回顾了一下以前写过的笔记。从今天开始进入多线程的知识点咯~ 我其实也是相...

    HtmlCssJs 评论0 收藏0
  • 一起并发编程 - 线程Join分析

    摘要:当时,会进入循环,系统会判断主线程是否处于活跃状态,如果处于活跃状态,主线程就会不停的等待。 由于前段时间比较忙,线程这快学习停滞了,只能利用周日的时间来写写博客了,多线程Join方法的作用就是把指定的线程加入到当前线程,让主线程等待子线程结束之后才能继续运行,从而完成同步操作 介绍 join() 的作用:让主线程等待子线程结束之后才能继续运行,首先先来看下以采集为案例的代码,统计采...

    happen 评论0 收藏0
  • 一起并发编程 - 处理异常中止的线程

    摘要:在之前,不能为线程单独设置或指定一个默认的,为了设置,需要继承并覆写方法。幸运的是后线程提供了一个方法,用来捕获并处理因线程中抛出的未知异常,以避免程序终止。 在单线程的开发过程中,通常采用try-catch的方式进行异常捕获,但是这种方式在多线程环境中会显得无能为力,而且还有可能导致一些问题的出现,比如发生异常的时候不能及时回收系统资源,或者无法及时关闭当前的连接... 概述 Ja...

    zacklee 评论0 收藏0
  • 一起并发编程 - sleep与wait的差异

    摘要:一般差异简单来说,是一个用于线程同步的实例方法。暂停当前线程,不释放任何锁。用来线程间通信,使拥有该对象锁的线程等待直到指定时间或。执行对该对象加的同步代码块。 在JAVA的学习中,不少人会把sleep和wait搞混,认为都是做线程的等待,下面主要介绍下这俩者是什么,及了解它们之间的差异和相似之处。 一般差异 简单来说,wait()是一个用于线程同步的实例方法。因为定义在java.l...

    noONE 评论0 收藏0

发表评论

0条评论

junnplus

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<