创建线程的第一种方式:继承Thread类(含Thread类部分源码解析)

2023-10-28

创建线程的第一种方式:继承Thread类

  1. 定义一个类,继承Thread类
  2. 重写Thread类中的run方法
  3. 创建Thread类的实现类的对象
  4. 利用该对象调用Thread类中的start方法

运行结果

好了,接下来我们看源码。

首先我们看一下Thread类。

可以看到Thread类实现了Runnable接口,这是另外一种创建线程的方式,我们会在之后的章节中给出。

在类声明的下面是静态代码块,也就是static{}包裹的部分,里面的代码会在类加载的时候执行一次,之后便不会在执行。

我们可以来尝试一下:

第一种,主类和静态代码块分开的情况:

运行结果是:

第二种:静态代码块所在的类也是主程序类,即:

运行结果和上次的不一样了:

发现了吗,静态代码块在还没有创建实例的时候就执行了,所以静态代码块是在类加载的时候创建的,而不是初始化。

好了,课外补充时间结束,我们回到Thread正题中来。

Thread中的静态代码块表示注册该Thread类,只会注册一次,这是防止Thread类未被注册的,也就是程序要确保注册是要做的第一件事。

Thread类中的变量和方法有:

Eclipse中查找一个类中所有的变量和方法的快捷键:Ctrl+O

Private volatile String name

这表示是线程的名字

我们可以在创建线程的时候修改名字:

如果我们没有指定名字,他会有默认的名字:

我们先来看看我们平时用无参Thread创建Thread对象的过程:

我们再来看看Thread类中的start方法

 

以上继承Thread类重写run方法创建线程并执行的过程。

可以发现,Thread类中很多都调用了底层的本地方法。

之后再来看看Thread类中有什么方法:

有两个init方法,都是private类型的,也就是说,除非是Thread类中的方法调用这两个方法,否则其他外部的类是无法调用该方法的。(不过应该可以用反射的方式调用,只要设置了权限,或者说暴力反射,也就是setAccessible)

Init方法是创建Thread对象的核心方法,我们使用的new Thread最终都调用的该方法进行创建。

该类重写了Object类的clone方法(也就是复制方法)(Java中所有的类隐式的继承Object类),他不允许克隆线程,用户调用该方法会返回一个不能克隆的异常。

Thread类中有很多个构造方法,如下:

Public static native Threa currentThread():返回当前正在执行的线程

Public static native void yield():提示系统当前线程愿意让步,也就是从执行状态回到就绪状态,但是注意,它有可能又马上就执行了.

Public static native void sleep(long):让当前线程休眠即毫秒

Public static void sleep(long,int):让当前线程休眠几毫秒加及纳秒

Protected Object clone():克隆,重写Object类的方法,禁止克隆,克隆方法一调用就抛不能克隆的异常(CloneNotSupportException)

Public synchronized void start():开启线程,如果启动失败会重新启动

Private native void start0(),启动线程实际调用的方法,是本地方法

Public void run():我们重写的Run方法,注意,如果我们是用继承Thread类的方法实现的话,他就是我们重写的方法,如果我们传入了Runnable实现类对象,他就是实现类对象中的run方法。

Private void exit():这个方法把一切包括线程组全部置为NULL

Public final void stop():终止线程的方法,已过时,因为不安全,他不管线程目前什么状态,执行了什么,执行完了没有,直接就把人扔出去了,所以不安全

Public void interrupt():这个方法说是终止线程,但是发现无论是使用在Run方法还是Main方法中,都不能阻止线程执行,但是如果线程陷入睡眠状态,比如Sleep的话,使用该方法会抛出InterruptException异常

比如:

package com.lqy.JavaBasics.Multithreading.Creation;

//测试Thread类中的interrupt能不能终端线程,然而无论是放在Run方法中还是Main方法中都不能

/*

 * interrupt方法用于打断sleepwaitjoin的线程,interrupt不会真的中断线程,他的本质只是设置一个中断状态,让该被打断的线程决定如何结束

 * 当线程处于sleepwaitjoin等阻塞状态时,interrupt方法会清空打断状态并抛出InterruptException异常

 * 我们需要注意的是,Java中凡是抛出InterruptException的方法(再加上Thread.interrupted)都会在抛出异常的时候,将interrupt Flag重新置为False

 * 这也就是说,当一个线程B被中断(被使用interrupt方法)的时候正处于睡眠状态(比如Sleep),他就会结束Sleep状态并且抛出InterruptException异常,并且,将中断位标记位False

 * 此时我们再去检查interrupt Flag的状态,他是False,不能正面他被中断了,现在唯一能证明当前线程B被中断的证据就是抛出的InterruptException异常,如果我们没有捕获处理直接扔掉的话

 * 恭喜你,你绝对不知道你的程序都经历了什么

 * 简而言之,interrupt方法只是将线程的中断位标记为True,表示是要中断的,但是中不中断,不管

 * 但是你可以使用了这个方法后检查中断位,检查到中断位为True的,停止线程,至于那些睡眠状态的,我们就捕获InterruptException异常,在Catch块中终止

 * 至于具体的怎么用它来中断线程,我们在TestInterrupted类中解释

 */

public class TestInterrupt extends Thread{

    int i=0;

    public void run() {

        try {

           //单位为毫秒。一秒等于1000毫秒

           Thread.sleep(1000000);

        } catch (InterruptedException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

        }

//      for(int i=0;i<1000;i++) {

//         System.out.println(i);

//         try {

//             Thread.sleep(1000);

//         } catch (InterruptedException e) {

//             // TODO Auto-generated catch block

//             e.printStackTrace();

//         }

           if(i==10) {

               this.interrupt();

           }

//      }

    }

   

    public static void main(String[] args) {

        TestInterrupt testInterrupt=new TestInterrupt();

        testInterrupt.start();

        testInterrupt.interrupt();

    }

}

运行结果:

public class TestInterrupted extends Thread{

    public void run() {

        for(int i=0;;i++) {

           System.out.println(i);

//         this.interrupt();

          

           //注意:interrupted使用后会清空当前判断位,也就是说无论之前是True还是False,使用过一次后就变回False

           //但是isInterrupted不会清空当前状态

           //比如下面两句输出中,interrupted使用输出后,if语句就永远不会为True

//         System.out.println(this.interrupted());

//         System.out.println(this.isInterrupted());

//         if(this.isInterrupted()) {

//             System.out.println("线程终止了");

//             break;

//         }

           if(this.interrupted()) {

               System.out.println("线程终止了");

               break;

           }

        }

    }

    public static void main(String[] args) {

        TestInterrupted testInterrupted=new TestInterrupted();

        testInterrupted.start();

        testInterrupted.interrupt();

    }

}

这样,方法中就能中断线程了

其实主要就是设置一个状态位,状态位为True就停止执行,因为线程无法结束就是因为重写的Run方法里面有死循环,如果没有的话,在运行完毕后,线程会自动停止,就和方法执行完毕一样,所以我们需要终止线程,其实就是退出循环。

我这里说这一点的原因是希望用的时候明白,使用这两种方式之后,不是直接就退出了Run方法,如果你只是退出了循环,循环之后的Run方法内的代码依然会执行,比如:

我将Run方法增加一条

public void run() {

        for(int i=0;;i++) {

           System.out.println(i);

//         this.interrupt();

          

           //注意:interrupted使用后会清空当前判断位,也就是说无论之前是True还是False,使用过一次后就变回False

           //但是isInterrupted不会清空当前状态

           //比如下面两句输出中,interrupted使用输出后,if语句就永远不会为True

//         System.out.println(this.interrupted());

//         System.out.println(this.isInterrupted());

//         if(this.isInterrupted()) {

//             System.out.println("线程终止了");

//             break;

//         }

           if(this.interrupted()) {

               System.out.println("线程终止了");

               break;

           }

          

        }

        System.out.println("我是循环后面的,Run方法内的代码");

    }

执行结果

为什么我一直强调这一点?因为如果你跳出当前死循环后下面还有一个死循环的情况下,你要考虑清楚你是要退出第一个死循环还是退出Run方法,之后将If语句块中的内容做相应的更改

Public static Boolean interrupted():获取当前线程的执行状态位,注意,获取之后会重置状态位。

Public Boolean isInterrupted():获取当前线程的执行状态位,获取之后不会重置状态位。

Private native Boolean isInterrupted(Boolean):获取某个线程的中断状态位,上面两种方法实际都是调用这个

Public void destroy():销毁线程的方法,已过时,不建议使用,容易产生死锁

Pubic final native Boolean isAlive():测试此线程是否处于活跃状态

Public final void suspend():如果线程处于活跃状态,则挂起线程,已过时,会产生死锁,不推荐使用

Public final void resume():恢复被挂起的线程,已过时,容易产生死锁,不推荐使用

Public final void setPriority(int):设置线程优先级,1-10,最低为1,最高为10,注意,不是优先级最高就一定优先执行,只是概率更大了

Public final int getPriority():获取当前线程的优先级,默认我们创建线程的优先级为5

Public final synchronized void setName(String):设置线程名字

Public final String getName():获取线程名字

Public final ThreadGroup getThreadGroup():获取当前线程所属的线程组

Public static int activeCount():获取活动线程的数量

Public static int enumerate(Thread[]):将当前活动的线程全部拷贝到指定数组中

Public native int countStackFrames():统计当前线程的堆栈帧数,线程必须暂停,已过时

Public final synchronized void join(long):等待多少毫秒线程才能运行(1秒等于1000毫秒

Public final synchronized void join(long,int):等待多少毫秒,再加上多少纳秒(1秒等于1000000000纳秒

Public final vois join():阻塞其他活跃线程,直到该线程执行完毕。这里没有添加sychronized关键字是因为他直接调用了join(long)方法,参数传过去为0

Public static void dumpStack():将当前线程的堆栈跟踪打印到标准错误流,此方法仅用于调试。

比如:

Public final void setDaemon(boolean):将此线程标记为守护线程

Public final Boolean isDaemon():检测此线程是否是守护线程

Public final void checkAccess():看看当前线程有没有修改此线程的权限

Public String toString():重写了ToString方法(Object类中的),使其直接输出的话可以展示线程名字,线程等级和所属线程组名字。

Public ClassLoader getContextClassLoader():获取上下文类加载器,注意:该方法前面有一个@CallerSensitive注解,因为Reflection.getCallerClass()要求调用者必须有@CallerSensitive注解并且拥有权限,比如Class.forName()方法上就有这个注解

该注解解释来源于:Java注解之@CallerSensitive - 风停了,雨来了 - 博客园 (cnblogs.com)

Public void setContextClassLoader(ClassLoader):自己设置类加载器

Public static native Boolean holdsLocak(Object):当且仅当当前线程在指定的对象(也就是那个Object参数)上保持监视器锁的时候返回True。说人话就是判断线程有没有持有那个锁。

Public StackTraceElement[] getStackTrace():返回当前线程的堆栈跟踪

Public static Map<Thread,StackTraceElement[]> getAllStackTraces():返回所有活动线程的堆栈跟踪信息。

好了,差不多就是这些了,最后,我们来说一下我们常用的四个休眠线程的方法:

Sleep(),wait(),yield()和join()

首先明白,wait方法是Object类的,其他三个是Thread类的

这四个方法Sleep ,wait ,yield方法是本地方法,注意join不是本地方法,它是调用了wait方法

Sleep有两个重载方法,分别是sleep(long)和sleep(lon,int)

Join和wait有三个,分别是join(),join(long),join(long,int)和wait(),wait(long),wait(long,int)

虽然这三个方法都是有不止一个重载方法,但是最终其实都调用了只有Long类型参数的方法。

首先,我们来说一下yield()方法

Yield()方法没有参数,它表示的是告知系统当前线程愿意让步,也就是说,当前线程获取了CPU资源后又释放了CPU资源,但是需要注意的是,当前线程没有阻塞,它直接回到就绪队列中等待CPU资源,这也就意味着,下一次获取CPU资源有可能还是这个线程。而且注意,这个方法它不会释放锁,也就是说,假如有一个Synchronized锁,而当前线程在得到了这个锁后放弃了CPU资源,锁不会释放,即便下一个线程得到CPU,如果没有那个锁,也依然只是阻塞在那里而已

Sleep()方法

Sleep是指让当前线程睡眠几毫秒或者几毫秒+几纳秒。注意,和yeild不一样,它是进入阻塞状态,阻塞了固定时间后才进入就绪状态,注意,它也不会释放锁

Sleep(0)是休眠0秒的意思

Wait()方法

Wait方法是Object类的方法,意思也是让线程休眠固定的时间,但是和sleep方法不同的是,它会释放锁Wait(0)的意思是一直阻塞,但是我们可以用notify或者notifyAll方法唤醒

Join()方法

Join实际上是调用了wait方法,这也就意味着它也会释放锁,但是和wait不一样的是join的无参构造方法,wait的无参构造相当于wait(0),但是join的无参构造是将所有在活动状态中的线程陷入阻塞,直到调用join()方法的线程全部执行完毕

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

创建线程的第一种方式:继承Thread类(含Thread类部分源码解析) 的相关文章

随机推荐

  • python数据可视化第八章使用matplotlib绘制高级图表

    使用matplotlib绘制高级图表 1 绘制高等图 等高线图是地形图上高程相等的相邻各点所连成的j闭合曲线 它会将地面上海拔高度相同的点连成环线 之后将环线垂直投影到某一水平面上 并按照 定的比例缩绘到图纸上 常见于山谷 山峰或梯度下降算
  • CloudOS:物联网开发平台,云上开发,边端交付

    什么是物联网 物联网 Internet of Things 简称IoT 是指通过各种信息传感器 射频识别技术 全球定位系统 红外感应器 激光扫描器等各种装置与技术 实时采集任何需要监控 连接 互动的物体或过程 采集其声 光 热 电 力学 化
  • iOS开发中,使用PC查看/修改沙盒文件

    简介 在进行iOS开发中 APP尤其是游戏 在沙盒中可以存放游戏资源包或者数据 例如Json文件 AssetBundle包 在使用服务器下载资源包 存放到本地沙盒中 可以大大减少APP本身的大小 当然 在调试的时候 如果频繁的更换服务器的A
  • 处理告警“ warning #69-D integer conversion resulted in truncation”的方法

    今天分享一个常见的语法告警 就是实参与形参的类型参数对于不上 错误之处 首先我们先看一下告警提示 告警 c 49 warning 69 D integer conversion resulted in truncation 其翻译过来意思就
  • 二 动态SQL和多对一,一对多

    二 动态SQL和多对一 一对多 10 多对一处理 测试环境搭建 按照查询嵌套处理 子查询 按照结果嵌套处理 联表查询 11 一对多 按照结果嵌套处理 按照查询嵌套处理 小结 12 动态 SQL IF choose when otherwis
  • xpath下载安装——Python爬虫xpath插件下载安装(2023.8亲测可用!!)

    目录 1 免费下载插件链接 若失效评论区留言发送最新链接 2023 7亲测可用 2 安装插件 1 打开chrome浏览器页面 点击 右上角三个点 gt 扩展程序 gt 管理拓展程序 若没找到点更多工具 2 打开右上角开发者模式 3 将刚刚下
  • 关于maven创建java-web工程加载过慢如何解决问题

    如何解决maven创建java web工程加载过慢如何解决问题 在创建项目时在这个界面中 添加设置一组变量archetypeCatalog internal 然后点击完成继续创建就可以了 但是这样做每次创建项目都要重复添加 很麻烦 如果大家
  • Spring实现封装自定义注解@Trimmed清除字符串前后的空格

    在Spring中实现字符串清除的方法有很多 原生方法String自带trim 方法 或者使用StringUtils提供的trim 方法 通常可以将上面的方式封装成自定义注解的形式去实现来节省更多的业务代码 Trimmed java impo
  • Foggy_driving数据集下载以及将其转换成VOC数据格式

    Foggy driving数据集官方下载地址 百度网盘下载链接 https pan baidu com s 1q4dhnlX doxlt13Mi uFZQ 提取码 2ap3 VOC格式的Foggy driving数据集百度网盘下载链接 ht
  • html基础题目

    1 在html中 样式表按照应用方式可以分为三种类型 其中不包括 d A 内嵌样式表 B 行内样式表 C 外部样式表文件 D 类样式表 2 在HTML中 可以使用 d 标记向网页中插入GIF动画文件 A
  • 从零开始实现自己的Kalimba——Cocos Creator新手教程系列(一)使用瓦片图Tiledmap设计游戏地图

    瓦片图Tiledmap可能是很多2d游戏开发者的偏爱 本节就Cocos Creator如何使用瓦片图进行详细的讲解 Tiled地图编辑器的下载安装不再赘述 下面介绍如何使用地图编辑器 创建新地图 地图方向选正常 其中45度和等角 交错 适合
  • uview2.0封装http请求实战以及常见请求传参实录

    1 前言 2 使用步骤 2 1 配置请求拦截器以及api集中管理配置 2 2 main js中进行引入请求拦截器 2 3 页面中引入请求方法并使用 1 前言 uview2 0是uniapp开发中使用频率相对来讲比较高的一款框架 今天从实战角
  • python爬虫十二:初步使用Scrapy框架爬取数据

    1 通过pipelines操作拿到的数据 要点一 爬虫文件必须通过关键字yield生成器才能与pipelines建立链接才可以操作 当运行爬虫文件之后执行的顺序如下图简介 如果重写模块或者内部方法可能会造成麻烦 往下翻阅可以看到open s
  • UC测试实习生笔试面试

    笔试 2014年5月14号 中大公教C402 一个半钟 好紧的时间 除了选择题和编程题 其他都感觉不好啊 一 选择题 1 chmod 755 filename 之后 文件权限为 A rwxr xr x 2 403状态表示 3 用于转义的字符
  • Python3网络爬虫--爬取百度搜索结果(附源码)

    文章目录 一 准备工作 1 工具 二 思路 1 爬虫思路 2 数据抽取思路 三 源代码 四 结果 五 总结 今天更新一篇基础 使用Python爬取百度搜索结果 最后将爬取结果保存到txt文本文件中 一 准备工作 1 工具 1 Google
  • java线程安全问题分析、3种解决办法

    目录 一 线程不安全 1 1 代码 1 2 解析 解析 卖出不存在的票 解析 卖出相同的票 1 3 解决办法 二 解决方法1 同步代码块 2 1 代码 2 2 解析 三 解决方法2 同步方法 3 1 代码 3 2 说明 四 解决方法3 Lo
  • php如何实现mac端重签ipa,mac用终端对ipa包重新签名

    一年前写过一个用命令行对安卓的apk包重新签名的文章 网上是让用IResin这个软件 但是我用了总是重签名失败 报错warning resource rules has been deprecated in Mac OSX gt 10 10
  • Qml-------ApplicationWindow

    QML内的窗口元素 ApplicationWindow和window 此文章讲解一下ApplicationWindow的基础使用 导入模块 QtQuick Controls qt两个模块的定义与使用方法 import QtQuick Con
  • std::jthread与std::thread区别

    std jthread是C 20新引入的线程类 与 std thread 类似 或者说 jthread是对thread进一步的封装 功能更强大 std jthread的 j实际上是 joining的缩写 众所周知 std thread在其生
  • 创建线程的第一种方式:继承Thread类(含Thread类部分源码解析)

    创建线程的第一种方式 继承Thread类 定义一个类 继承Thread类 重写Thread类中的run方法 创建Thread类的实现类的对象 利用该对象调用Thread类中的start方法 运行结果 好了 接下来我们看源码 首先我们看一下T