转载

Thread源码剖析

前言

昨天已经写了:

  • 多线程三分钟就可以入个门了!

如果没看的同学建议先去阅读一遍哦~

在写文章之前通读了一遍《Java 核心技术 卷一》的并发章节和《Java并发编程实战》前面的部分,回顾了一下以前写过的笔记。从今天开始进入多线程的知识点咯~

我其实也是相当于从零开始学多线程的,如果文章有错的地方还请大家多多包含,不吝在评论区下指正呢~~

一、Thread线程类API

声明本文使用的是JDK1.8

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

顶部注释的我们已经解析过了,如果不知道的同学可前往: 多线程三分钟就可以入个门了!

1.1设置线程名

我们在使用多线程的时候,想要查看线程名是很简单的,调用 Thread.currentThread().getName() 即可。

如果没有做什么的设置,我们会发现线程的名字是这样子的: 主线程叫做main,其他线程是Thread-x

下面我就带着大家来看看它是怎么命名的:

Thread源码剖析

nextThreadNum() 的方法实现是这样的:

Thread源码剖析

基于这么一个变量--> 线程初始化的数量

Thread源码剖析

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

Thread源码剖析

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

Thread源码剖析

下面我们来测试一下:

  • 实现了Runnable的方式来实现多线程:
public class MyThread implements Runnable {
    
    @Override
    public void run() {
        // 打印出当前线程的名字
        System.out.println(Thread.currentThread().getName());
    }
}

测试:

public class MyThreadDemo {
    public static void main(String[] args) {


        MyThread myThread = new MyThread();

        //带参构造方法给线程起名字
        Thread thread1 = new Thread(myThread, "关注公众号Java3y");
        Thread thread2 = new Thread(myThread, "qq群:742919422");


        thread1.start();
        thread2.start();
        
        // 打印当前线程的名字
        System.out.println(Thread.currentThread().getName());
    }
}

结果:

Thread源码剖析

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

Thread源码剖析

检查是否有权限修改:

Thread源码剖析

至于threadStatus这个状态属性, 貌似没发现他会在哪里修改

Thread源码剖析

1.2守护线程

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

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

守护线程有一个 特点

  • 当别的用户线程执行完了,虚拟机就会退出,守护线程也就会被停止掉了。
  • 也就是说:守护线程作为一个 服务线程,没有服务对象就没有必要继续运行

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

  1. 在线程启动前 设置为守护线程,方法是 setDaemon(boolean on)
  2. 使用守护线程 不要访问共享资源 (数据库、文件等),因为它可能会在任何时候就挂掉了。
  3. 守护线程中产生的新线程也是守护线程

测试一波:

public class MyThreadDemo {
    public static void main(String[] args) {


        MyThread myThread = new MyThread();

        //带参构造方法给线程起名字
        Thread thread1 = new Thread(myThread, "关注公众号Java3y");
        Thread thread2 = new Thread(myThread, "qq群:742919422");

        // 设置为守护线程
        thread2.setDaemon(true);

        thread1.start();
        thread2.start();
        System.out.println(Thread.currentThread().getName());
    }
}

上面的代码运行多次可以出现(电脑性能足够好的同学可能测试不出来): 线程1和主线程执行完了,我们的守护线程就不执行了 ~

Thread源码剖析

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

Thread源码剖析

1.3优先级线程

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

线程的优先级是 高度依赖于操作系统的 ,Windows和Linux就有所区别(Linux下优先级可能就被忽略了)~

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

Thread源码剖析

实现:

Thread源码剖析

setPriority0 是一个本地(navite)的方法:

 private native void setPriority0(int newPriority);

1.4线程生命周期

在上一篇介绍的时候其实也提过了线程的线程有3个基本状态:执行、就绪、阻塞

在Java中我们就有了这个图,Thread上很多的方法都是 用来切换线程的状态 的,这一部分是重点!

Thread源码剖析

其实上面这个图是不够完整的, 省略 掉了一些东西。后面在讲解的线程状态的时候我会重新画一个~

下面就来讲解与线程生命周期相关的方法~

1.4.1sleep方法

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

Thread源码剖析

于是乎,我们的图就可以补充成这样:

Thread源码剖析

1.4.2yield方法

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

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

Thread源码剖析

于是乎,我们的图就可以补充成这样:

Thread源码剖析

1.4.3join方法

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

Thread源码剖析

我们进去看看 具体的实现

Thread源码剖析

wait方法是在Object上定义的,它是native本地方法,所以就看不了了:

Thread源码剖析

wait方法实际上它也是 计时等待(如果带时间参数) 的一种!,于是我们可以补充我们的图:

Thread源码剖析

1.4.3interrupt方法

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

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

  • 被终止的线程B会立即释放锁,这可能会让 对象处于不一致的状态
  • 线程A也不知道线程B什么时候能够被终止掉 ,万一线程B还处理运行计算阶段,线程A调用stop方法将线程B终止,那就很无辜了~

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

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

  • 要注意的是:interrupt**不会真正停止 一个线程,它仅仅是给这个线程发了一个 信号*告诉它,它应该要结束了(明白这一点非常重要!)
  • 也就是说:Java设计者实际上是 想线程自己来终止 ,通过上面的 信号 ,就可以判断处理什么业务了。
  • 具体到底中断还是继续运行,应该 由被通知的线程自己处理
Thread t1 = new Thread( new Runnable(){
    public void run(){
        // 若未发生中断,就正常执行任务
        while(!Thread.currentThread.isInterrupted()){
            // 正常任务代码……
        }
        // 中断的处理代码……
        doSomething();
    }
} ).start();

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

我们来看看源码是怎么讲的吧:

Thread源码剖析

再来看看刚才说抛出的异常是什么东东吧:

Thread源码剖析

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

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

  • 静态方法interrupted()--> 会清除中断标志位
  • 实例方法isInterrupted()--> 不会清除中断标志位

Thread源码剖析

Thread源码剖析

上面还提到了,如果阻塞线程调用了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();
        }
    };
}

结果:

Thread源码剖析

接下来我们分析它的 执行流程 是怎么样的:

Thread源码剖析

2018年4月18日20:32:15(哇,这个方法真的消耗了我非常长的时间).....感谢@开始de痕迹的指教~~~~~

该参考资料:

  • https://www.cnblogs.com/w-wfy/p/6414801.html
  • https://www.cnblogs.com/carmanloneliness/p/3516405.html
  • https://www.zhihu.com/question/41048032/answer/89478427
  • https://www.zhihu.com/question/41048032/answer/89431513

二、总结

可以发现我们的图是还没有补全的~后续的文章讲到同步的时候会继续使用上面的图的。在Thread中 重要的还是那几个可以切换线程状态的方法 ,还有理解中断的真正含义。

使用线程会导致我们数据不安全,甚至程序无法运行的情况的,这些问题都会再后面讲解到的~

之前在学习操作系统的时候根据《计算机操作系统-汤小丹》这本书也做了一点点笔记,都是比较 浅显的知识点 。或许对大家有帮助

  • 操作系统第一篇【引论】
  • 操作系统第二篇【进程管理】
  • 操作系统第三篇【线程】
  • 操作系统第四篇【处理机调度】
  • 操作系统第五篇【死锁】
  • 操作系统第六篇【存储器管理】
  • 操作系统第七篇【设备管理】

参考资料:

  • 《Java核心技术卷一》
  • 《Java并发编程实战》
  • 《计算机操作系统-汤小丹》

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以 关注微信公众号:Java3y 。为了大家方便,刚新建了一下 qq群:742919422 ,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

原文  http://zhongfucheng.bitcron.com/post/duo-xian-cheng/threadyuan-ma-pou-xi
正文到此结束
Loading...