【后端开发】JavaEE初阶—Theard类及常见方法—线程的操作(超详解)

news2024/11/15 15:55:23

前言:

🌟🌟本期讲解多线程的知识哟~~~,希望能帮到屏幕前的你。

🌈上期博客在这里:【后端开发】JavaEE初阶—线程的理解和编程实现-CSDN博客

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

目录

📚️1.引言

📚️2.Thread常见的构造方法

2.1创建对象名字

1.第一种创建线程对象,并命名

2.使用Runnable对象,并命名

3.检查线程名字

 2.2线程组

 📚️3.Theard类常见属性

3.1属性ID与名称

3.2是否后台线程

 3.3是否存活

 📚️4.启动线程

4.1多次启动线程

 4.2实现多次启动线程

4.3start()与run()使用

📚️5.终止一个线程

5.1设置标志位isQuit

5.2标志位的替代

 📚️6.等待线程

📚️7.获取线程引用

7.1使用Thread继承

7.2不使用Thread继承 

 📚️8.总结

 

📚️1.引言

 Hello!!! 小伙伴们咱们又见面啦,继小编上期讲解了多线程编程的相关重要知识,以及如何实现线程的创建之后,本期将继续讲解关于多线程的相关方法和属性,开始发车了加油加油~~~🥳🥳🥳

且听小编讲解,包你学会!!!

📚️2.Thread常见的构造方法

主要方法如下:

这里的创建线程对象,以及使用Runnable对象创建线程小编是在上期是讲解过的,想要了解的小伙伴可以去小编主页看看;那么接下来就来几个陌生的方法吧

2.1创建对象名字

1.第一种创建线程对象,并命名

代码如下:

Thread t=new Thread(()->{
                System.out.println("这是thread线程");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
        },"我的线程");

 注意:小编这里使用了lambda表达式进行演示,其中在方法体内进行了补充,在方法体后,用双引号表示的就是为这个线程所编写的名字;

2.使用Runnable对象,并命名

代码如下:

Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {

            }
        },"这是我的线程");

注意:此时小编没有对run方法进行重写,但是仍然对线程进行了命名;

3.检查线程名字

这里就要用到JDK中的jconsole了,这个在JDK中存在;

然后点击进入jconsole,并进行我们创建的线程的连接,图片展示:

很明显这里小编创建的项目是threadDemo6,并点击连接,找到线程,就可以发现我们创建线程的名字了,图片展示如下:

这就是我们创建线程的名字啦~~~

注意:在执行上述操作时,一定要保证从开始到结束中,idea上的线程代码要跑起来,否则在连接处就无法找到我们创建的线程; 

当然这里也可以使用getName()方法,小编会在后面演示~~~

 2.2线程组

这里的线程组是java中的概念,和系统内核中的线程组是不一样的;

 线程组的作用:

1. 组织线程:可以将多个线程归为一个线程组,方便对相关线程进行整体操作和管理。
2. 控制权限:线程组可以控制其包含的线程的访问权限,例如设置是否允许某个线程组中的线程修改系统资源等。
3. 异常处理:当一个线程组中的某个线程抛出未捕获的异常时,线程组可以对这个异常进行统一的处理。

这里的线程组,咱们了解即可~~~

 📚️3.Theard类常见属性

常见属性如下:

这里的getState()方法就是描述线程的状态,进程存在就绪状态与阻塞状态,那么线程也存在对应的状态,以及这里的getPriority()方法描述的是线程的调度优先级,但是效果不明显,至于收否被中断,后面小编会进行讲解;接下来即小编重点实例讲解的了;

3.1属性ID与名称

如何获取线程的ID与名称;代码如下:

Thread t=new Thread(()->{
                System.out.println("这是thread线程");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
        },"我的线程");
t.start();
 System.out.println(t.getId()+"  "+t.getName());

注意:在通过Theard实例调用线程的ID和名字时,对应的是JVM自动分配的身份标识,对于名字一般是Thread-0,Thread-1......但是自主给线程起名后,对应的名字也会变为所起名字;

3.2是否后台线程

按照之前的理解,当main函数执行结束后,整个函数就应该执行结束了,但是在多线程中并不是如此,因为JVM内置线程为后台线程,而后台线程不会阻止线程结束

我们创建的代码线程默认为前台线程,前台线程会阻止线程结束,所以即使main函数执行完了,那么线程仍然不会结束此时;

此时我们就要将前台线程改为后台线程,代码如下:

 public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                }
            }
        }, "这是我的线程");

        // 在 start 之前, 设置线程为 后台线程 (不能在 start 之后设置)
        t.setDaemon(true);

        t.start();
    }

此时主线程走完了就不会有任何输出:

注意:前台线程会阻止进程结束,而后台线程不会阻止进程结束,并且在改写前台线程为后台线程时,必须在创建线程之前!!!

 3.3是否存活

这里的是否存活表示的是内核中的PCB线程是否存在;

Thread实例,虽然来说表示的是一个线程,但是这里和内核中的PCB线程的生命周期是不一样的

代码实例如下:

 Thread t=new Thread(()->{
                System.out.println("这是thread线程");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
        },"我的线程");
        System.out.println("线程创建之前"+t.isAlive());//false
        t.start();
        System.out.println("线程启动之后"+t.isAlive());//true
        Thread.sleep(3000);//保证t线程结束
        System.out.println("t线程结束之后"+t.isAlive());//false

这里的输出结果就如同上述注解;

注意:内核中的PCB线程是在t调用start方法后创建线程才会存在,当线程中run()方法执行完后,内核PCB被释放了,此时isAlive表示为没有存活;

这里的主线程休眠是为了保证另一线程执行完毕;

 📚️4.启动线程

对于启动线程来说就使用start方法就行了,但是这里要进行扩展

4.1多次启动线程

代码如下:

Thread t=new Thread(()->{
                System.out.println("这是thread线程");
                
        },"我的线程");
        t.start();
        t.start();

这里小编进行了线程的两次启动,那么结果就是:

很明显这里抛出异常了即:非法的线程状态异常~~~

注意:线程只能启动一次,一个线程实例对象,多次调用start方法就会抛出非法线程状态异常

 4.2实现多次启动线程

由于上述讲到,一个线程实例化对象不能多次启动线程,那么对于多次启动线程,就要实现多个线程,多个启动

代码实例如下:

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println("hello1");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("hello2");
        });
        t1.start();
        t2.start();
    }

此时我们创建了两个线程,这里就可以实现多次启动线程啦~~~

4.3start()与run()使用

对于这两种方法,两者其实是互不相干的;

start()方法:是通过系统调用API实现了一个线程的创建,其中的JVM在创建好线程后自动调用run方法,此时就有多个线程;

run()方法:就是之前在JavaSE部分中,类中方法的调用一致,但是这里调用后,没有创建新的现场,仍然为单线程;

代码实例:

class MyThread4 extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
            
        }
    }
}

public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread t = new MyThread4();
        t.start();
        // t.run();

        while (true) {
            System.out.println("hello main");
            
        }
    }

注意:在如上述代码中,当调用start方法后,会创建两个线程,那么会不断输出(hello thread)和(hello main)这两个输出交替出现,但是调用run方法,就会陷入死循环打印(hello thread)不会向下执行主线程了;

📚️5.终止一个线程

这里终止一个线程就是:让线程run方法执行完毕;

5.1设置标志位isQuit

代码如下:

 public static boolean isQuit=true;
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (isQuit){
                System.out.println("这是一个thread线程,在工作");
                try {
                    Thread.sleep(2000);
                }catch (InterruptedException e){
                    throw new RuntimeException();
                }
            }
            System.out.println("线程结束");
        });
        t.start();
        Thread.sleep(4000);
        System.out.println("让t线程结束");
        isQuit=false;

此时的输出结果就是:

此时我们想让线程结束,那么就要将循环内的判断条件进行改变,即将isQuit的值变为false,并且为了观察,进行必要的输出打印;

 那么我们将代码中的isQuit改为主线程的局部变量,就是不可行的!!!

因为lambda表达式/匿名内部类存在变量捕获的问题,此时这里的isQuit已经被final修饰了,并且匿名内部类是可以访问外部类的成员变量的,所以不可以改为main函数的局部变量;

5.2标志位的替代

这里就用Thread.currentThread().isInterrupted()来代替isQuit,但是这里表示为false,interrupt表示修改值

代码如下:

 public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("线程在工作");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                   
                }
            }
            System.out.println("工作结束");
        });
        t.start();
        Thread.sleep(4000);
        System.out.println("结束线程工作");
        t.interrupt();//类似改变标志的布尔值

    }

此时我进行结果打印时,发现抛出异常了:

当我们输出结束线程工作时,这里没有结束继续工作,并抛出异常:休眠终止异常~~~

注意:

在执行 sleep时调用interrupt会导致sleep提前唤醒;会导致抛出上述异常,或者将Thread实例中的isInterrupted()标志位,改为false,这就会导致程序继续执行;

解决方法 :

此时我们就可以加上break,让线程立即结束,代码如下:

 while (!Thread.currentThread().isInterrupted()){
                System.out.println("线程在工作");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                   // break;
                }
            }

当然或者直接省去sleep休眠状态,直接一直工作知道终止线程;

经过以上解释,程序的终止是一种软性操作,需要线程的配合才能实现!!!

 📚️6.等待线程

由于线程是调度执行的,底层调度是不确定的,但是可以通过一些API来影响线程的执行顺序,此时join就提供了这样的操作;

代码如下:

 public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            int n = 5;
            for (int i = 0; i < n; i++) {
                System.out.println("我是一个线程, 正在工作中...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行结束");
        });

        t.start();
        // 这个操作就是线程等待.
        t.join();
        System.out.println("这是主线程, 期望这个日志在 t 结束后打印");
    }

 那么此时小编就使用了join来等待线程的执行完毕,再输出主线程,输出结果:

那么此时达到了我们的要求;但是我们运用sleep方法也可以实现,为啥不用它呢???

解答:在使用sleep方法时要知道线程结束的具体时间,因为休眠时间是要根据线程执行完毕才能够实现的,而使用join就不必知道这一点;

注意:

在使用join方法时要进行异常的抛出;

在那个线程使用join方法,谁就处于等待状态,而调用的线程实例就是被等待的一方;

使用sleep方法可以实现上述需求,但是需要知道线程的执行时间,所以不推荐;

join中也可以添加等待时间,如果不添加就是“死等”状态,一般在计算机中不推荐死等

 这里也可以使用线程等待来实现一系列运算;

public static long sum=0;
    public static void main(String[] args)throws InterruptedException {
        Thread t1=new Thread(()->{
            for (long i = 0; i <50 ; i++) {
                sum+=i;
            }
        });
        Thread t2=new Thread(()->{
            for (long i = 50; i <=100 ; i++) {
                sum+=i;
            }
        });
        t1.start();
        t2.start();
        long begin=System.currentTimeMillis();
        t1.join();
        t2.join();
        long end=System.currentTimeMillis();
        System.out.println("sum="+sum);
        System.out.println("等待时间"+(end-begin)+"ms");
    }

这里小编就使用多线程,和等待机制来实现算数相加,此时在两个线程开启后,在让主线程进行等待,这里使用时间打印可以来表示单个线程和多个线程来执行这个任务的时间相差,小编这里就不再过多演示;

📚️7.获取线程引用

7.1使用Thread继承

代码如下:

 public static void main(String[] args) throws InterruptedException {
        Thread t=new mythread7();
        t.start();   
    }
}
class mythread7 extends Thread{
    @Override
    public void run(){
        System.out.println(this.getId()+" "+this.getName());
    }
}

注意:在Thread继承中可以使用this拿到线程实例;

7.2不使用Thread继承 

代码如下:

 public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            Thread t1=Thread.currentThread();
            System.out.println(t1.getId());
        });
        t.start();
    }

注意:如果是Runnable或者是lambda表达式,this就无法指向Thread对象,这个时候就要用到Thread.currentThread()

至于为啥不直接使用(t)来得到 属性:

在 Lambda 表达式中,变量必须是最终变量或有效最终变量,而 t 是一个非最终变量,因为他还没有完全初始化

 📚️8.总结

💬💬小编本期讲解了关于线程的某些重要属性和方法,例如线程的启动,终止,等待以及常见的属性的获取方法和构造方法,并附上代码供小伙伴们参考~~~

代码在这里哟:GGBondlctrl/Thread (gitee.com)😊 😊 😊

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                                                                 😊😊  期待你的关注~~~

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

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

相关文章

Vue3新组件transition(动画过渡)

transition组件&#xff1a;控制V-if与V-show的显示与隐藏动画 1.基本使用 <template><div><button click"falg !falg">切换</button><transition name"fade" :enter-to-class"etc"><div v-if"falg&quo…

【开源服务框架】Dubbo

&#x1f384;欢迎来到边境矢梦的csdn博文&#x1f384; &#x1f384;本文主要梳理Java面试中开源服务框架Dubbo会涉及到的知识点 &#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一…

C++标准库容器类——string类

引言 在c中&#xff0c;string类的引用极大地简化了字符串的操作和管理&#xff0c;相比 C 风格字符串&#xff08;char*或cahr[]&#xff09;&#xff0c;std::string 提供了更高效和更安全的字符串操作。接下来让我们一起来深入学习string类吧&#xff01; 1.string 的构造…

sqli-lab靶场学习(三)——Less8-10(盲注、时间盲注)

Less8 第八关依然是先看一般状态 http://localhost/sqli-labs/Less-8/?id1 然后用单引号闭合&#xff1a; http://localhost/sqli-labs/Less-8/?id1 这关的问题在于报错是不显示&#xff0c;那没办法通过上篇文章的updatexml大法处理。对于这种情况&#xff0c;需要用“盲…

【C++笔记】C++编译器拷贝优化和内存管理

【C笔记】C编译器拷贝优化和内存管理 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】C编译器拷贝优化和内存管理前言一.对象拷贝时的编译器优化二.C/C内存管理2.1练习2.2 C内存管理方式2.3 operator new与operator…

线程池动态设置线程大小踩坑

在配置线程池核心线程数大小和最大线程数大小后&#xff0c;如果调用线程池setCorePoolSize方法来调整线程池中核心线程的大小&#xff0c;需要特别注意&#xff0c;可能踩坑&#xff0c;说不定增加了线程让你的程序性能更差。 ThreadPoolExecutor有提供一个动态变更线程池核心…

.bixi勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复

导言 在当今数字化时代&#xff0c;勒索软件已成为企业和个人面临的重大安全威胁。.bixi勒索病毒作为其中一种新型恶意软件&#xff0c;以其快速加密文件的能力和高效传播机制引发了广泛关注。该病毒不仅能够迅速锁定用户的关键数据&#xff0c;还常常在感染后施加极大的心理压…

【devops】devops-ansible之介绍和基础使用

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

微信公众号文章自动化排版实现思路

文章目录 一、前言1.1 个人写作的痛点1.2 自动化排版工具实现的功能 二、我的文章创作与发布流程三、公众号文章自动化排版实现思路3.1 Typora软件展示3.2 96微信公众号排版平台展示3.3 自动化排版实现思路3.3.1 先构建一个空的文章html模版3.3.2 读取md文章&#xff0c;对内容…

大学生请码住!分享10款AI论文工具搞定论文开题到答辩全过程!

在当今学术研究和论文写作领域&#xff0c;AI工具的出现极大地提高了写作效率和质量。对于大学生来说&#xff0c;这些工具不仅能够帮助他们快速生成高质量的论文初稿&#xff0c;还能进行内容优化、查重和排版等操作。以下是10款值得推荐的AI论文工具&#xff0c;其中特别推荐…

STL-set/multiset关联式容器

目录 一、常见接口 1.0 迭代器 1.1 构造函数 1.2 增删查 1.3 查找和统计 二、multiset 2.1 构造 2.2 查找 2.3 删除 2.4 统计 关联式容器是⽤来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;关联式容器逻辑结构通常是⾮线性结构&#xff0c;两个位置有紧密…

社交电商中“信任”基础与“链动 2+1 模式 O2O 商城小程序”的价值探索

摘要&#xff1a;本文深入探讨了在基于社交的商业模式中&#xff0c;“信任”作为重要基础条件的关键作用。详细分析了在产品同质化日益严重的当下&#xff0c;人与人之间口口相传的宣传方式优势。同时&#xff0c;全面引入“链动 21 模式 O2O 商城小程序”&#xff0c;深入阐述…

Java面试篇-AOP专题(什么是AOP、AOP的几个核心概念、AOP的常用场景、使用AOP记录操作日志、Spring中的事务是如何实现的)

文章目录 1. 什么是AOP2. AOP的几个核心概念3. AOP的常用场景4. 使用AOP记录操作日志4.1 准备工作4.1.1 引入Maven依赖4.1.2 UserController.java4.1.3 User.java4.1.4 UserService.java 4.2 具体实现&#xff08;以根据id查询用户信息为例&#xff09;4.2.1 定义切面类&#x…

整合多方大佬博客以及视频 一文读懂 servlet

参考文章以及视频 文章&#xff1a; 都2023年了&#xff0c;Servlet还有必要学习吗&#xff1f;一文带你快速了解Servlet_servlet用得多吗-CSDN博客 【计算机网络】HTTP 协议详解_3.简述浏览器请求一个网址的过程中用到的网络协议,以及协议的用途(写关键点即可)-CSDN博客 【…

[数据结构]无头单向非循环链表的实现与应用

文章目录 一、引言二、线性表的基本概念1、线性表是什么2、链表与顺序表的区别3、无头单向非循环链表 三、无头单向非循环链表的实现1、结构体定义2、初始化3、销毁4、显示5、增删查改 四、分析无头单向非循环链表1、存储方式2、优点3、缺点 五、总结1、练习题2、源代码 一、引…

Mysql----索引与事务

1.索引 1.1什么是MYSQL的索引 MySQL官方对于索引的定义&#xff1a;索引是帮助Mysql高效获取数据的数据结构 Mysql在存储数据之外&#xff0c;数据库系统中还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种引用&#xff08;指向&#xff09;表中的数据&…

萤石云平台接入SVMSPro平台

萤石云平台接入SVMSPro平台 步骤一&#xff1a;进入萤石云官网&#xff1a;https://open.ys7.com/ &#xff0c;点右上角的登陆&#xff0c;填写自己的用户名密码&#xff1b; 步骤二&#xff1a;登陆进去后&#xff0c;开发者服务—>我的账号—>应用信息&#xff0c;在…

电气自动化入门07:开关电源、三相异步电动机多地与顺序控制电路

视频链接&#xff1a;3.5 电工知识&#xff1a;三相交流异步电动机多地与顺序控制及开关电源选型_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1PJ41117PW?p9&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.开关电源功能与选型说明&#xff1a; 2.三相异步电动机…

数据结构与算法之间有何关系?

相信很多人都应该上个《数据结构与算法》这门课吧&#xff0c;而这两个概念也如孪生兄弟一样经常被拿出来一起讨论。那它们究竟是一个什么样子的关系呢&#xff1f; 听到数据结构与算法我第一反应是想到了Pascal 语言之父尼古拉斯沃斯在他的《Algorithms Data Structures Pro…

esp32s3分区表配置及读写

一、分区表介绍 每片 ESP32-S3 的 flash 可以包含多个应用程序&#xff0c;以及多种不同类型的数据&#xff08;例如校准数据、文件系统数据、参数存储数据等&#xff09;。因此&#xff0c;在 flash 的 默认偏移地址 0x8000 处烧写一张分区表。 分区表中的每个条目都包括以下…