Thread类的基本用用法

news2024/11/30 7:42:54

1.创建线程

1.1继承Thread类

线程创建需要Thread类但是不需要import导入是为什么?

因为java.lang默认import不需要导入,java.lang中包含Thread

为什么在MyThread类中只能使用try catch 无法使用throws?

因为父类Thread run中没有throws

 class Demo {
    public static class MyThread extends Thread{
        public void run(){
            while(true){
                System.out.println("hello world");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread t = new MyThread();
        //创建线程
        t.start();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

 结果:一直循环下去hello main 和 hello world谁先谁后都有可能,这是两个线程调度顺序是随机的,无法预测谁先执行谁后执行。而且是由操作系统内核调度器去控制的咋们没法在应用程序中编写代码去控制(调度器没有提供api),唯一能做的就是给线程设置优先级,但是优先级对于操作系统来说也是仅供参考,不会严格的定量遵守。

此时我们可以去JDK的bin中的jconsole.exe查看调度

 点击不安全连接

 此时我们就可以看到main的状态

1.2 实现Runnable接口

MyRunnable runnable = new MyRunnable()就是一个任务要执行的逻辑,最终还是要通过Tread真正创建线程,线程通过Runnale来表示而不是直接重写Thread run来表示,使用Runnable主要是为了解耦合使代码之间的关联关系降低方便以后进行更改,我们以后写代码就是要写成低耦合(模版之间的依赖尽量少)高内聚(一个模版之内有关联得东西放在一起)。

第一个线程:

第二个线程:

 main方法对应的线程就是进程至少包含的那个线程即主线程,run方法相当于回调函数,与java中的compareTo 和 compare一样也是属于回调函数(该函数不用自己去调用而是交给别人调用)

1.3使用匿名内部类

本质上是方法一为啥叫匿名内部类 

这样可以少定义一些类了,如果代码是一次性的就可以使用匿名内部类

1.4使用Runable匿名内部类

使用Runnab 任务和线程是分离的,降低耦合度

1.5 引入Lambda表达式

本质上是一个匿名函数,最主要的用途就是作为回调函数

这种方法不需要重写run方法

2.Thread类

2.1Thread类及其常见方法

关于命名的应用:

2.2Tread 的几个常见属性

Daemon“是否后台线程”意思“是否守护线程”

后台线程:JVM自带的线程他们的存在不影响进程结束,如果线程要结束了他们也随之结束

前台线程:前台线程的存在能够影响到进程继续存在,这样的线程就是前台线程,相当于t1 main线程虽然结束了但是t1还在,所以进程仍然存在。

进程之间也有父子关系:IDEA本身也是一个java进程,又创建出一个新的java进程这两个进程是父子关系,线程之间不存在父子关系。

如果把线程设为后台线程无力阻止后台线程结束,比如下面的例子:

关于isAlive(),java代码中创建的Thread对象和系统的线程是一一对应的关系,但是Thead对象的生命周期和系统的线程生命周期是不同的(可能存在Thread对象还存活,但是系统中的线程已经销毁的情况)

虽然是存在3S但是打印了4个true,此处打印出来的结果可能是三个true也可能是4个true,因为不同的线程是随机调度的我们无法决定,同时主线程的四次打印和t线程的结束谁先谁后也不一定

同时补充ThreadGroup 线程组,这个是把多个线程放在一个组里面统一针对这个线程组里面所有的线程进行一些属性设置

运用:

 2.3 启动一个线程 start()

java标准库/jvm提供的方法本质上是调用操作系统的API

ctrl点击start会在源代码里面发现只能看到以下这样的代码,这样的方法没有实现只是一个声明,native代表的是本地方法,实现是在JVM内部通过c++来实现的

同时每个Thread对象只能start一次,每次创建一个新的线程都给创建一个新的Thread对象(不能重复利用) 。

2.4终止一个线程

中断后面可能还会恢复此处使用终止更为合适,终止线程不会再恢复。那如何终止一个线程呢?终止就是让线程结束,我们使用的方法是让线程的入口方法执行完毕(即让run方法尽快return),线程就随之结束了。

示例:

public class Demo {
    private static boolean isFinished = false;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while (!isFinished){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("thread线程结束");
        });
        t.start();
        Thread.sleep(3000);
        isFinished = true;    
    }
}

 结果:

 那我们如果把定义的变量放在里面会如何?显然是不会成功启动线程,那为什么不会成功启动呢?

此处鼠标停留会显示错误由上面一句我们可以知道,它提示我们在lambda里面的变量应该是fianl或者实际上是final,那这句话是什么意思呢?fianl修饰的变量是不可修改的,但是我们没加fianl也没真的修改,此处涉及的问题其实就是关于变量捕获的问题

lambda里面希望使用外面的变量,触发变量捕获这样的语法,lambda是回调函数执行时机是很久之后(操作系统真正创建出线程之后才会执行)很可能后续线程创建好了,当前main这里的方法都执行完了,对应的isFinished就销毁了为了解决这个问题java的做法是

把被捕获的变量给拷贝一份如下所示,拷贝给lambda里面,外面的变量是否销毁就不影响lambda里面的执行了(相比之下,c++的做法更加野蛮,c++要程序员自习保证,你lambda里访问的变量生命周期有效)c++还能让你选择,是不是要拷贝)

拷贝就意味着这样的变量就不适合进行修改,修改一方,另一方不会随之变化(本质上是两个变量)这种一边变一边不变,可能会给程序员带来更多的困惑,java大佬们就想了一个方法,压根不允许你这里进行修改,所以上述代码会一直循环下去

如果是引用类型引用类型本身不能修改(不能修改引用类型指向其他的对象),但是引用指向的对象本体是可以修改的。对象本体的生命周期是jvm垃圾回收管理的(GC),不会随着main方法执行完毕就销毁这样的说法。

示例如下

为什么把代码改成成员变量就可以修改呢?

  把上述代码改成成员变量,此时不再是变量捕获语法,而是切换成“内部类访问外部类的成员”语法,lanbda本质上是函数式接口相当于一个内部类,isFinished变量本身就是外部类的成员,内部类本来就可以访问外部类的成员,成员变量生命周期也是让GC来管理,在lambda里面不担心变量生命周期失效的问题,也就不必拷贝,也就不比限制fianl之类的

lanbda是定义在new Thread之前,也是在Thread t声明之前

 关于线程终止java的Thread对象提供现成的变量直接进行判定,不需要自己创建了,但是while循环里面不能使用t.isInterrupted,由上述lambda的定义可知t无法引入成员,关于java自带的终止如下所述,但是运行代码会发现代码会报错。

代码报错是因为t.interrupt()不只是修改boolean变量的值,它同时还会唤醒sleep,线程在执行的时候绝大多数的时间是休眠的当sleep被唤醒就会被上面的catch捕获到触发RuntimeException(e)抛出InterruptedException的异常

针对这个异常我们的处理方法是,使用break结束循环

 

如果不加上述break空着呢会怎样? t线程会无法终止,针对上述代码其实是sleep在搞鬼,正常来说调用Interrupt方法会被修改isInterruptted方法内部的标志位设为true,由于上述代码中是把sleep给唤醒了,唤醒后isInterruptted标志位给设置回false,因此在这样的情况下,如果继续执行到循环的条件判定,就会发现能够继续执行。java中的线程终止不是一个强制性的措施,不是mian让t终止,t就一定终止选择权在t自己手上(看t线程自己的代码咋写)

 2.5等待一个线程--join

多个线程之间并发执行随机调度,join能够要求多个线程之间结束的先后顺序,比如在主线程中调用t.join就是让主线程等待t线程先结束。虽然可以通过sleep休眠的时间来控制线程结束的顺序,但是有的情况下这样设定并不科学。有的时候就是希望t先结束,mian就可以紧跟着结束了,此时通过设置时间的方式不一定靠谱。

在mian线程调用t.join效果,让mian线程等待t先结束,当执行到t.join此时main线程就会阻塞等待,一直等到,t 线程执行完毕,join才能继续执行,只要t 线程不结束,主线程的join就会一直一直等待下去,但是join也提供了带参数的版本,指定“超时时间”等待的最大时间

带有超时时间的等待,才是更科学的做法,计算机中尤其是和网络通信相关的逻辑,一般都是需要超时时间

2.6 休眠当前线程

休眠一个线程使用sleep,有一点要记住因为线程的调度是不可控,所以这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间。写了sleep(1000)实际上可能比1000多一点,代码调用sleep,相等于让当前线程让出CPU的资源,后续时间到的时候,需要操作系统内核把这个线程重新调到CPU上才能继续执行,时间到了意味着允许调度了,而不是立即执行了,对于操作系统是毫秒级别的时间,同时还有sleep(0)这样的用法,写了sleep(0)意味着让当前的线程立即放弃CPU资源,等待操作系统重新调度,把cpu让出来给别人更多执行的机会

2.7线程状态 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2215487.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 日志打印

使用日志打印: private static Logger log LoggerFactory.getLogger(DeptController.class);RequestMapping("/depts")public Result list() { // System.out.println("查询全部部门数据");log.info("查询全部部门数据");ret…

Spring Boot:为中小型医院网站提速

3 系统分析 3.1 可行性分析 通过对本基于Spring Boot的中小型医院网站实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、操作可行性、经济可行性和时间可行性四方面进行分析。 3.1.1 技术可行性 本基于Spring Boot的中小型…

[LeetCode] 118. 杨辉三角

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1]] 示例 2: 输入: numRows 1 输出: [[1…

【计网】从零开始理解UDP协议 --- 理解端口号和UDP结构

我依旧敢和生活顶撞, 敢在逆境里撒野, 直面生活的污水, 永远乐意为新一轮的月亮和日落欢呼。 --- 央视文案 --- 从零开始理解UDP协议 1 再谈端口号2 理解UDP 报头结构3 UDP 的特点4 UDP 的缓冲区5 UDP 使用注意事项 1 再谈端口号 之前我…

个性化图像生成新SOTA!阿里开源MIP-Adapter,可将无需微调的IP-Adapter推广到同时合并多个参考图像。

今天给大家介绍阿里最近开源的个性化图像生成的新方法MIP-Adapter,将无需微调的预训练模型(IP-Adapter)推广到同时合并多个参考图像。MIP-Adapter会根据每个参考图像与目标对象的相关性来给这些图像分配不同的“重要性分数”。这样&#xff0…

OpenShift 4 - 云原生备份容灾 - Velero 和 OADP 基础篇

《OpenShift 4.x HOL教程汇总》 说明: 本文主要说明能够云原生备份容灾的开源项目 Velero 及其红帽扩展项目 OADP 的概念和架构篇。操作篇见《OpenShift 4 - 使用 OADP 对容器应用进行备份和恢复(附视频) 》 Velero 和 OADP 包含的功能和模…

十、Python基础语法(循环-while循环)

什么是循环&#xff1f;在满足条件的情况下,让指定的代码重复执行 。 一、while循环 while是python中的关键字&#xff0c;作用是用来实现循环的。 语法&#xff1a; 需求&#xff1a; 打印10次“我爱学习” a 0while a < 10:print(我爱学习)a 1运行结果&#xff1a;…

C++(类和对象—对象特性)

对象的初始化和清理 生活中我们买的电子产品都基本会有出厂设置&#xff0c;在某一天我们不用时候也会删除一些自己信息数据保证安全。 C中的面向对象来源于生活&#xff0c;每个对象也都会有初始设置以及对象销毁前的清理数据的设置。 1.构造函数和析构函数 对象的初始化和清理…

ROS理论与实践学习笔记——5 ROS机器人系统仿真之URDF、Gazebo与Rviz综合应用

6.1 机器人运动控制以及里程计信息显示 &#xff08;1&#xff09;ros_control 简介 场景&#xff1a;如何在不同的机器人系统上实现同一套 ROS 程序的部署&#xff1f;例如&#xff0c;在开发阶段&#xff0c;为了提高测试效率&#xff0c;程序通常在仿真平台&#xff08;如 …

vue2 Canvas 多边形区域绘制组件封装

效果预览&#xff1a; CanvasBox组件 <!-- 区域设置canvas --> <template><div class"all" ref"divideBox"><!-- <div><button click"test">清空</button></div> --><img id"img"…

FineReport 标题冻结,冻结表头,冻结行列

先进行重复标题行和重复标题列设置&#xff0c;然后再进行分页冻结设置 1、冻结列 SELECT * FROM S人员花名册选定列 – 右击 – 设置重复标题列 2、冻结行 选定行 – 右击 – 设置重复标题行 3、重复与冻结设置 模板 – 重复与冻结设置 冻结重复标题有分页冻结和填报…

Leecode刷题之路第20天之有效的括号

题目出处 20-有效的括号-题目出处 题目描述 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 1.左括号必须用相同类型的右括号闭合…

GraphRAG:知识图谱+RAG技术

由于RAG的一些缺陷&#xff0c;最近工作中用到了GraphRAG&#xff0c;可以简单理解为知识图谱RAG工作&#xff0c;在进行QFS与深度推理上表现良好。老板希望&#xff0c;在类似于库存管理、预测上面&#xff0c;可以结合更多的上下文信息和逻辑关系&#xff0c;进行推理和结果的…

一文带你了解,2024年世界职业院校技能大赛该如何备赛

2024年世界职业院校技能大赛&#xff08;以下简称“大赛”&#xff09;即将拉开帷幕&#xff0c;这不仅是一次展示职业院校学生专业技能的舞台&#xff0c;更是促进国际职业教育交流与合作的重要契机。为了确保参赛队伍能在比赛中取得优异成绩&#xff0c;以下是一些具体建议&a…

【第2章 开始学习C++】函数

文章目录 导语使用有返回值的函数函数变体用户定义的函数用户定义的有返回值的函数 导语 函数用于创建 C 程序的模块&#xff0c; 对 C 的 OOP 定义至关重要。 C 函数分两种&#xff1a; 有返回值的和没有返回值的。 使用有返回值的函数 有返回值的函数将生成一个值&#x…

MySQL SELECT 查询(二):复杂查询的实现

MySQL SELECT 查询&#xff08;二&#xff09;&#xff1a;复杂查询的实现 文章目录 MySQL SELECT 查询&#xff08;二&#xff09;&#xff1a;复杂查询的实现1. 多表查询1.1 常见错误&#xff1a;笛卡尔积与属性归属1.2 连接条件与规范1.3 连接类型1.4 SQL99 连接特性 2. SQL…

C++红黑树(简单易懂)

C红黑树 红黑树红黑树的概念 红黑树节点的定义红黑树的插入颜色变化红黑树的插入拷贝构造红黑树的验证全部代码实现红黑树与AVL树的比较红黑树的应用 &#x1f30f;个人博客主页&#xff1a;个人主页 红黑树 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在…

Java并发 - 线程池

文章目录 总体设计常见线程池FixedThreadPoolCachedThreadPoolSingleThreadPoolThreadPoolExecutor 核心参数工作原理生产者消费者模型创建线程池提交任务任务提交方式任务提交流程executeaddWorker Worker队列线程运行 runWoker获取任务销毁工作线程线程池关闭shutdown/shutdo…

维修数据屏:重塑热力公司运维管理新格局

在热力公司的运维管理中&#xff0c;高效的报修和维修流程是确保系统稳定运行的关键。随着科技的发展&#xff0c;维修数据屏的出现为热力公司的运维工作带来了重大变革。 一、传统热力运维面临的挑战 过去&#xff0c;热力公司在报修和维修方面存在诸多问题&#xff0c;给运维…

基于Java的超市管理系统(源码+定制+解答)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…