Java并发06:一网打尽线程属性

83 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

学习MOOC视频记录的笔记

线程的各个属性

什么时候我们需要设置守护线程?

我们应该如何应用线程优先级来帮助程序运行?有哪些禁忌?

不同的操作系统如何处理优先级问题?

1.线程各属性纵览

属性名称用途
编号(ID)每个线程有自己的ID,用于标识不同的线程
名称(Name)作用让用户或程序员在开发、调试或运行过程中,更容易区分每个不同的线程、定位问题等
是否是守护线程(isDaemon)true代表该线程是【守护线程】,false代表线程是非守护线程,也就是【用户线程】
优先级(Priority)优先级这个属性的目的是告诉线程调度器,用户希望哪些线程相对多运行、哪些少运行,

2.线程Id

每个线程都有自己的ID,并且这个ID是不能修改的。线程的ID是从1开始自增的,main函数就是第一个线程。

/**
* ID从1开始,JVM运行起来后,我们自己创建的线程的ID早已不是0.
*/
public class Id {
    public static void main(String[] args) {
        Thread thread = new Thread();
        System.out.println("主线程的ID: " + Thread.currentThread().getId());  // 1
        System.out.println("子线程的ID: " + thread.getId());  // 20
    }
}

输出:

主线程的ID: 1
子线程的ID: 20

源码:

初始值为0,线程ID从1开始【先++再返回】。

/* For generating thread ID */
private static long threadSeqNumber;
 
private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}

为什么子线程ID不是按顺序编号的?

因为除了主线程之外,还有很多其他的线程早就被建立的,比如:

image-20221105151520039

Signal Dispatcher: 将操作系统信号发送给适当的程序

Reference Handler: 与GC引用相关

Finalizer: 负责执行对象 finalize 的方法

3.线程名字

一个好的习惯,在创建一个线程的时候就为其执行名字,这样在后期打印日志排除问题的时候比较容易定位。

  • 默认线程名字源码分析
public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
 
/* For autonumbering anonymous threads. */
private static int threadInitNumber;
 
// 加锁修饰不会重名
private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}
  • 修改线程的名字(代码演示、源码分析)
public final synchronized void setName(String name) {
    checkAccess();
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }
 
    // 线程内部属性,可以修改,Java层面的
    this.name = name;
    if (threadStatus != 0) {
        // 线程启动起来就不能修改了
        setNativeName(name);
    }
}

4.守护线程

作用:给用户线程提供服务

看线程是否会阻止JVM的停止

代码执行完毕,即JVM发现所有的用户线程都执行完毕了,JVM就会和守护线程一起结束,守护线程的作用就是给其他线程提供便利服务,比如垃圾处理器。

3个特性:

  1. 线程类型默认继承自父线程:自己创建的线程是用户线程,这是因为我们的父线程就是用户线程,守护线程创建的线程继承自己的父线程,setDaemon 可以将一个用户线程设置为守护线程。
  2. 被谁启动:通常守护线程是由JVM自动启动的,而不是由用户启动的,在JVM启动时会有一个非守护线程,即main线程
  3. 不影响JVM退出:JVM想要退出时只会看有没有用户线程,不会看有没有守护线程,因为守护线程一直都是存在的。

守护线程和普通线程的区别:

  • 整体区别,都是用来执行代码的线程
  • 唯一区别在于JVM的离开,用户线程会影响JVM是否退出,守护线程不会。

5.线程优先级

10个级别,默认5

/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
 
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
 
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;

程序设计不应依赖于优先级

  • 不同操作系统不一样
  • 优先级会被操作系统改变

6.各属性总结

属性名称用途注意事项
编号(ID)标识不同的线程被后续创建的线程使用;唯一性;不允许被修改
名称(Name)定位问题清晰有意义的名字;默认的名称
是否是守护线程(isDaemon)守护线程、用户线程二选一;继承父线程;setDaemon
优先级(Priority)相对多运行默认和父线程的优先级相等,共有10个等级,默认值是5;不应依赖

7.面试常见问题

  1. 守护线程和普通线程的区别?

整体上没有太大区别,区别在于是否会影响到JVM的退出,以及他们的作用不同,用户线程是执行逻辑的,守护线程是服务我们的。

  1. 我们是否需要给线程设置为守护线程?

不应该将自己的线程设置为守护线程。如果设置可能会导致JVM异常退出关闭,从而产生数据不一致的情况。

  1. 什么时候我们需要设置守护线程?

通常情况不需要设置,JVM所提供的守护线程足够我们使用

  1. 我们应该如何应用线程优先级来帮助程序运行?有哪些禁忌?

不应该使用优先级来帮助程序运行,因为不同的操作系统政策不一样

  1. 不同的操作系统如何处理优先级问题

不同操作系统对优先级的映射和调度都不一样,还有可能被忽略