一、Thread线程类API

实现多线程的本质上都是对Thread类来进行操作的,我们来看看Thread类一些重要的知识点,Thread这个类很大,不可能把整个看下来,我们只能了解一些常见的重要的算法。

1.1 设置线程名

我们使用多线程的时候,想要查看线程名是很简单的,直接调用

Thread.currentThread().getName()

  

java Thread 源码分析 thread源码解析_java Thread 源码分析

如果没有做说明多余的设置,我们会发现线程的名字是这样的,主线程叫main,其他线程是Thread-x如上图所示

我们来看看源码,了解一下为什么会这么命名

java Thread 源码分析 thread源码解析_System_02

其中nextThreadNum()的实现方法是这样的

java Thread 源码分析 thread源码解析_守护线程_03

至于这个变量threadInitNumber------->线程初始化数量

java Thread 源码分析 thread源码解析_java Thread 源码分析_04

再点进去看看init方法就可以确定了

java Thread 源码分析 thread源码解析_优先级_05

看到这里,要是想为线程起个名字也是很简单的。Thread给我们提供了构造方法

java Thread 源码分析 thread源码解析_优先级_06

下面我们来测试一下

实现了Runnable的方式来实现多线程

public class MyThread extends Thread {
	
   @Override
    public void run() {
	   System.out.println(Thread.currentThread().getName());
        
    }
}

 测试:

public class MyThreadDemo {
    public static void main(String[] args) {
        
        MyThread my1 = new MyThread();
        
        Thread th1=new Thread(my1,"sb");
        Thread th2=new Thread(my1,"nb");
        my1.start();
        th1.start();
        th2.start();
      
    }
}

结果:

java Thread 源码分析 thread源码解析_System_07

当然我们还可以通过setName(String name)来改掉线程的名字的。我们来看看方法的实现

java Thread 源码分析 thread源码解析_守护线程_08

检查是否有权限修改

 

java Thread 源码分析 thread源码解析_优先级_09

1.2 守护线程

守护线程是为其他线程服务的

  垃圾回收线程就是守护线程

守护线程有一个特点

  当别的用户线程执行完了,虚拟机就会退出,守护线程也就被停止掉了

  也就是说,守护线程作为一个服务线程,没有服务对象就没必要继续运行了

使用线程的时候要注意的地方

  1、在线程启动前设置为守护线程,方法是setDaemon(boolean on)

     2、使用守护线程不要访问共享资源(数据块、文件等),因为它可能会在任何时候就挂掉了

  3、守护线程中产生的新线程也是守护线程

测试一下

 

public class MyThreadDemo {
    public static void main(String[] args) {
        
        MyThread my1 = new MyThread();
   
        Thread th1=new Thread(my1,"sb");
        Thread th2=new Thread(my1,"nb");
        th2.setDaemon(true);
        th1.start();
       
        System.out.println(Thread.currentThread().getName());
      
        th2.start();
    }
}

  

java Thread 源码分析 thread源码解析_System_10

 

 

 上面的代码运行多次可以出现,线程1和主线程执行完了,我们守护线程就不执行了。

 这也就是为什么我们要在启动之前设置守护线程了

java Thread 源码分析 thread源码解析_System_11

1.3 优先级线程

线程优先级仅仅表示线程获取的CPU时间片几率高,但这不是一个确定因素

线程的优先级是高度依赖于操作系统的

可以看到的是Java提供的优先级默认是5,最低1最高10

java Thread 源码分析 thread源码解析_守护线程_12

设置优先级

java Thread 源码分析 thread源码解析_java Thread 源码分析_13

setPriority0是一个本地的方法

private native void setPriority0(int newPriority);

1.4线程的生命周期

上一篇介绍了线程的基本状态有三个:执行、就绪和阻塞

Thread有很多方法都是来切换线程的状态的,这一部分是重点

1.4.1 sleep方法

调用sleep方法会进入计时等待状态,等事件到了,进入的是就绪态而不是运行态

java Thread 源码分析 thread源码解析_java Thread 源码分析_14

1.4.2 yield方法

调用yield方法会让别的线程执行,但是不保证真正让出

意思是:我有空,可以的话,让你们先执行

java Thread 源码分析 thread源码解析_java Thread 源码分析_15

1.34.3 join方法

调用join方法,会等待该线程执行完后才执行别的线程

java Thread 源码分析 thread源码解析_守护线程_16

进去看看具体实现

java Thread 源码分析 thread源码解析_java Thread 源码分析_17

wait方法是native方法,看不了

wait方法实际上它也是计时等待的一种

 

1.4.4 interrupt方法

线程中断在之前的版本有stop方法,但是被设置过时了,现在已经没有强制线程终止的方法了

由于stop方法可以让一个线程A终止掉另外一个线程B

被终止的线程B会立即释放锁,这可能会让对象处于不一致的状态

线程A也不知道线程B什么时候能够被终止掉,万一线程B还处理运行计算阶段,线程A调用Stop方法,将线程B终止掉,那就很无辜了

总而言之,Stop方法,太暴力了,不安全,所以被设置过时了

我们一般是用interrupt来请求终止线程

要注意的是:

  1、interrupt不会真正停止一个线程,它仅仅是给这个线程发了一个信号告诉它,它应该要结束了(这点很重要)

  2、也就是说,Java设计者实际上是想线程自己来终止,通过上面的信号就可以判断处理上面业务了

  3、具体到底是中断还是继续运行,应该由被通知的线程自己处理

Thread t1 = new Thread( new Runnable(){
    public void run(){
        // 若未发生中断,就正常执行任务
        while(!Thread.currentThread.isInterrupted()){
            // 正常任务代码……
        }
        // 中断的处理代码……
        doSomething();
    }
} ).start();

  再次:调用interrupt()并不是要真正的终止掉当前线程,仅仅是设置了一个中断标志,这个中断标志可以给我们来判断上面时候该干什么活,什么时候中断由我们自己决定,这样就可以安全的终止线程了。

再来看看源码是怎么写的

java Thread 源码分析 thread源码解析_守护线程_18

Java设计者设置中断表值的目的是想由被通知的线程自己处理

被阻塞的线程调用中断方法是不合理的,因为有可能造成中断无效

所以说,interrupt方法压根不会对线程状态造成什么影响,它仅仅是设置一个标志位罢了

interrupt线程中断还要另外两个方法(检查该线程是否被中断)

静态方法interrupted()会清除中断标志位

动态方法isInterrupted()不会清除中断标志位

上面还提到了,如果阻塞线程调用了interrupt()方法,那么会抛出异常,设置标志位为false,同时该线程会退出阻塞的。我们来测试一波:

 

public class Main {
    /**
     * @param args
     */
    public static void main(String[] args) {
        Main main = new Main();

        // 创建线程并启动
        Thread t = new Thread(main.runnable);
        System.out.println("This is main ");
        t.start();

        try {

            // 在 main线程睡个3秒钟
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            System.out.println("In main");
            e.printStackTrace();
        }

        // 设置中断
        t.interrupt();
    }

    Runnable runnable = () -> {
        int i = 0;
        try {
            while (i < 1000) {

                // 睡个半秒钟我们再执行
                Thread.sleep(500);

                System.out.println(i++);
            }
        } catch (InterruptedException e) {


            // 判断该阻塞线程是否还在
            System.out.println(Thread.currentThread().isAlive());

            // 判断该线程的中断标志位状态
            System.out.println(Thread.currentThread().isInterrupted());

            System.out.println("In Runnable");
            e.printStackTrace();
        }
    };
}

  

java Thread 源码分析 thread源码解析_优先级_19

二、总结

在Thread中重要的还是那几个可以切换线程状态的方法,还有理解中断的真正含义。