并发和多线程(五)--线程相关属性和线程异常处理

1、线程id和name

  线程id是线程的唯一标识,不可修改,而线程名称是可以修改的。

public static void main(String[] args) {
    Thread thread = new Thread();
    System.out.println("主线程ID为:"+Thread.currentThread().getId());
    System.out.println("主线程ID为:"+thread.getId());
    System.out.println("主线程name为:"+thread.getName());
    thread.setName("thread58");
    System.out.println("主线程name为:"+thread.getName());
}
结果:
主线程ID为:1
主线程ID为:12
主线程name为:Thread-0
主线程name为:thread58

  从结果看到,主线程的id为1,所以线程的id也是从1开始的,而新建的子线程的id为12,而不是我们猜想的2。

  通过查看源码,知道线程id的规则如下:

public long getId() {
    return tid;
}

//通过调用Thread构造器初始化Thread,而tid = nextThreadID()
public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

    
private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}

  这里是++threadSeqNumber实现自增,那为什么子线程的id不是2呢,是因为Jvm在运行代码的时候还会启动别的线程帮助程序运行和处理,例如垃圾收集器等,通过debug我们就可以看到。

   而线程的name值,只是对线程的命名,除了默认情况下的命名,我们还可以通过setName()修改,而且可以多个线程name相同。

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}

  通过源码看到,默认没有name的情况下,通过"Thread-" + nextThreadNum()进行命名,而这个方法是threadInitNumber++,所以从Thread-0开始。

 2、守护线程

   对于jvm来说,一般我们创建的线程都是用户线程,除了用户线程就是守护线程,守护线程为了程序运行而服务,守护用户线程。一般来说,守护线程被jvm启动,而用户线程被主线程启动。根本的区别就是,如果jvm发现没有当前没有用户线程在运行,jvm就会退出,守护线程对jvm退出没有影响。

  线程类型默认继承父线程,例如,在main方法中创建一个线程,主线程就是用户线程,所以被创建的线程默认也是用户线程。同样的,守护线程创建的线程也是守护线程。

//判断当前线程是否Wie守护线程
public final boolean isDaemon() {
    return daemon;
}
//设置当前线程为守护线程。true,守护线程,false,用户线程
public final void setDaemon(boolean on) {
    checkAccess();
    if (isAlive()) {
        throw new IllegalThreadStateException();
    }
    daemon = on;
}

PS:

  开发过程中,不要将线程设置为守护线程,一旦设置为守护线程,jvm发现没有用户线程在运行,就直接关闭了,就会导致我们的程序没有执行完就关闭了。

3、线程优先级

  线程优先级是指线程启动执行的优先级,对于Java来说,一共10个优先级,默认为5。准确的说,线程优先级也是继承父类优先级,如果你把主函数设置为8,新建的子线程默认也是8.

/**
 * 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;

//设置线程优先级
public final void setPriority(int newPriority) {
    
}

//获取线程优先级
public final int getPriority() {
    return priority;
}

   开发中,我们不应该依赖线程优先级,因为我们在使用多线程的时候,发现优先级高的线程不一定比优先级低的线程先执行,只能说概率更高而已。而且,1-10的优先级只是jvm的划分,总归要和OS挂钩的,不同的OS对优先级的划分不同,需要进行映射。例如Windows有7个优先级,1和2对应1,3和4对应2。。。而Linux系统下Java线程优先级会被忽略,不能起作用。在solaris系统中,又是不同的。

  设置线程优先级,还有可能带来的问题,就是某些优先级低的线程可能一直无法获得CPU使用权,也就是一直保持"饥饿"状态。所以,综上,完全不建议修改线程优先级。

4、如何捕获线程异常?

public static void main(String[] args) {
    Thread thread = new Thread(new ThreadClass());
    thread.start();
    for (int i = 0; i < 100; i++) {
        System.out.println(i);
    }
}

@Override
public void run() {
    throw new RuntimeException();
}

  通过上面的代码的运行,可以看到,子线程发生异常,主线程还是继续运行,如果运行在服务器上面,可能都不知道程序有出现过异常。所以,我们需要对Thread的代码进行异常处理,例如try catch。

try catch处理线程异常:

try {
    Thread thread = new Thread(() -> {
        throw new RuntimeException();
    });
    Thread thread1 = new Thread(() -> {
        throw new RuntimeException();
    });
    Thread thread2 = new Thread(() -> {
        throw new RuntimeException();
    });
    Thread thread3 = new Thread(() -> {
        throw new RuntimeException();
    });
    thread.start();
    thread1.start();
    thread2.start();
    thread3.start();
} catch (RuntimeException e) {
    System.out.println("当前线程发生异常");
}
结果:
Exception in thread "Thread-0" Exception in thread "Thread-3" Exception in thread "Thread-2" Exception in thread "Thread-1" java.lang.RuntimeException
    at com.diamondshine.Thread.MyRunnableClass.lambda$main$0(MyRunnableClass.java:18)
    at java.lang.Thread.run(Thread.java:745)
java.lang.RuntimeException
    at com.diamondshine.Thread.MyRunnableClass.lambda$main$2(MyRunnableClass.java:24)
    at java.lang.Thread.run(Thread.java:745)
java.lang.RuntimeException
    at com.diamondshine.Thread.MyRunnableClass.lambda$main$3(MyRunnableClass.java:27)
    at java.lang.Thread.run(Thread.java:745)
java.lang.RuntimeException
    at com.diamondshine.Thread.MyRunnableClass.lambda$main$1(MyRunnableClass.java:21)
    at java.lang.Thread.run(Thread.java:745)
View Code

  四个线程都发生异常,我们通过try catch去处理,发现子线程发生异常,并没有被catch捕获。原因是try catch可以捕获主线程的异常,却不能捕获子线程的异常,导致即使有了try catch也是不行的。正确的方式:

UncaughtExceptionHandler

//自定义UncaughtExceptionHandler

@Slf4j
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        log.error("当前线程:{}发生异常,异常信息为:{}", t.getName(), e.getMessage());
        //后续报警等相关逻辑
    }
}
public static void main(String[] args) {

    Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());

        Thread thread = new Thread(() -> {
            throw new RuntimeException();
        });
        Thread thread1 = new Thread(() -> {
            throw new RuntimeException();
        });
        Thread thread2 = new Thread(() -> {
            throw new RuntimeException();
        });
        Thread thread3 = new Thread(() -> {
            throw new RuntimeException();
        });
        thread.start();
        thread1.start();
        thread2.start();
        thread3.start();
    
}
结果:
16:02:42.288 [Thread-3] ERROR com.diamondshine.Thread.MyUncaughtExceptionHandler - 当前线程:Thread-3发生异常,异常信息为:
java.lang.RuntimeException: null
    at com.diamondshine.Thread.MyRunnableClass.lambda$main$3(MyRunnableClass.java:26)
    at java.lang.Thread.run(Thread.java:745)
16:02:42.288 [Thread-0] ERROR com.diamondshine.Thread.MyUncaughtExceptionHandler - 当前线程:Thread-0发生异常,异常信息为:
java.lang.RuntimeException: null
    at com.diamondshine.Thread.MyRunnableClass.lambda$main$0(MyRunnableClass.java:17)
    at java.lang.Thread.run(Thread.java:745)
16:02:42.288 [Thread-1] ERROR com.diamondshine.Thread.MyUncaughtExceptionHandler - 当前线程:Thread-1发生异常,异常信息为:
java.lang.RuntimeException: null
    at com.diamondshine.Thread.MyRunnableClass.lambda$main$1(MyRunnableClass.java:20)
    at java.lang.Thread.run(Thread.java:745)
16:02:42.288 [Thread-2] ERROR com.diamondshine.Thread.MyUncaughtExceptionHandler - 当前线程:Thread-2发生异常,异常信息为:
java.lang.RuntimeException: null
    at com.diamondshine.Thread.MyRunnableClass.lambda$main$2(MyRunnableClass.java:23)
    at java.lang.Thread.run(Thread.java:745)

  从结果看,我们自定义的异常处理器有效,成功的捕获到异常,这时候不仅仅打印日志,可以根据自己的使用场景进行逻辑处理,例如:让监控系统报警等。

原文地址:https://www.cnblogs.com/huigelaile/p/11749209.html