多线程Thread(初阶二:Thread类及常⻅⽅法)

news2024/11/16 9:37:28

目录

一、Thread 的常⻅构造⽅法

继承Thread代码:

实现Runnable接口代码:

二、Thread 的⼏个常⻅属性

1、id:

2、获取线程的名字。

3、进程的状态:

4、在java中设置的优先级,

5、是否后台线程,

6、是否存活,

7、是否中断,

8、等待线程(结束),

9、获取线程的引用,currentThread()


一、Thread 的常⻅构造⽅法

最后一个线程组的概念是java的概念,和系统中的线程组不一样,不是同一个东西。

第一个和第二个构造方法在初阶一有介绍,

链接:多线程Thread(初阶一:认识线程)-CSDN博客

第三是创建一个线程,并且可以给这个线程命名,并不是它默认的Thread-0 / 1 / 2.....,第四个也是可以给线程对象命名,不过是使用接口的方法。

继承Thread代码:

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("hello thread1");
    }
    String name;
    public MyThread(String name) {
        super(name);
        this.name = name;
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread("这是我的线程");
        t1.start();
        Thread t2 = new Thread("这是我的线程"){
            @Override
            public void run() {
                System.out.println("hello thread2");
            }
        };
        t2.start();
    }
}

执行效果:

两个线程:

上面两种的两种创建线程方式都可以。

实现Runnable接口代码:

class MyThread2 extends Thread {
    String name;
    public MyThread2(String name) {
        super(name);
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println("hello thread1");
    }
}
public class TestDemo2 {
    public static void main(String[] args) {
        Thread t1 = new MyThread2("这是我的线程1");
        t1.start();
        Thread t2 = new Thread("这是我的进程2") {
            @Override
            public void run() {
                System.out.println("hello thread2");
            }
        };
        t2.start();
    }
}

执行效果:

两个线程:

注意:我们创建的线程如果不起名字,默认是Thread-0 1 2 3....,给不同线程起不同名字,对于线程的执行,没啥影响,主要是为了方便调试;线程之间的名字是可以重复的,在同一个工作中,需要多个线程完成,都可以起一样的名字;但是名字也不要乱起,最后要有一定的描述性。


二、Thread 的⼏个常⻅属性

1、id

jvm自动分配的身份标识,会保证唯一性。

2、获取线程的名字。

3、进程的状态

就绪状态、阻塞状态;线程也有状态,Java中对线程的状态进行了进一步的区分(比系统原生的状态更丰富一些)。

4、在java中设置的优先级

效果不是很明显,因为系统是随机调度的(对内核的调度器调度过程会产生一些影响)。

5、是否后台线程

也称为是否守护线程(比较抽象),所以记住和理解是否后台线程会轻松一些,与此相反,也有前台线程(和Android上的前台app,后台app完全不同)。

后台线程和前台线程的区别前台线程的运行,会阻止进程结束

                                               后台线程的运行,不会阻止进程结束

我们创建的线程,默认是前台进程。

如下代码:

public class TestDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        t.start();
    }
}

执行效果:会不停的输出打印hello thread

那我们试着把这个线程设置为后台线程试试(默认为前台),代码:

public class TestDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        //在start之前,设置线程为后台线程(不能在start之后设置)
        t.setDaemon(true);
        t.start();
    }
}

执行效果:

根本就没有打印hello thread,进程就结束了,因为是后台进程,不会阻止进程的结束。

设为true是后台,后台,可以理解为背后,不出面的人,你感知不到;后台不会阻止进程结束。

不设为true是前台,前台,可以理解为明面上的人,你能感知到;前台会阻止进程结束。

6、是否存活,

isAlive( )表示内核中的线程(PCB)是否还存在。java代码中定义的线程对象(Thread)实现,虽然是表示一个线程,但这个对象本身的生命周期,和内核中的PCB生命周期  ,是不完全一样的。

isAlive的测试,代码:

public class TestDemo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println("start之前:" + t.isAlive());
        t.start();
        System.out.println("start之后" + t.isAlive());
        Thread.sleep(2000);
        System.out.println("t结束之后" + t.isAlive());
    }
}

执行效果:

解释:启动线程之前,也就是start之前,t这个实例线程是还没开始的,所以isAlive返回的是false,start后就查看这个线程存不存在,因为线程也刚启动,所以isAlive返回的是true,休眠两秒之后,t线程已经跑完了,所以isAlive返回的也是false。

7、是否中断

也是终止的意思。

下面写一个代码,用boolean类型的变量来控制while循环,也起到终止线程的作用。

public class ThreadDemo1 {
    private static boolean isQuit = false;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!isQuit) {
                System.out.println("我是一个线程,正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("线程工作完毕");
        });
        t.start();
        Thread.sleep(3000);

        System.out.println("让线程结束");
        isQuit = true;
    }
}

注意:这里的isQuit变量放在类中,不放在方法里作为局部变量的原因是涉及到了变量捕获如果放在main方法里,是局部变量,就必须要加 final 修饰将其变成常量为什么呢?这时因为main线程和我们创建的线程的栈帧生命周期不同,如果main线程先结束了,我们创建的线程要获取这个变量,但是我们main线程的栈帧生命周期已经结束了,我们拿不到这个变量,java这里的做法就比较暴力,直接把这个变量变成常量,要么这个变量就是全局变量,以至于不会发生上面的情况。

执行效果:

这时,我们也可以使用jdk里自带的方法:isInterrupted( )

代码如下:

public class ThreadDemo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("我是一个线程,正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行完毕");
        });

        t.start();

        Thread.sleep(3000);
        System.out.println("让线程结束");
        t.interrupt();
    }
}

执行效果:

这个线程并不会结束,会继续执行。

解释:可以看到这里抛出了个异常,而这个异常是InterruptedException 异常

如果没有sleep,interrupt是可以让线程顺利结束的,但是有了sleep就会引起变数。

这说明,sleep这出现问题了,为什么呢?原因是这个线程还在执行,main线程休眠3秒后,终止了这个线程,但sleep还没休眠够1秒,这里就会提前唤醒。

这里,提前唤醒,会做两件事:

1、抛出 InterruptedException (紧接着就会被catch获取到)。

2、清除 Thread 对象的 isInterrupt 标志位。(sleep清空标志位,是为了给程序猿更多的“可操作性空间”)。

所以,interrupt已经把标志位设置为true了,sleep被提前唤醒,清除了isInterrupt标志位后,就又把标志位设回了false,这个while循环还是能进去的,所以线程还在继续执行。

此时,程序猿就可以在catch语句中加入一些代码,来做一些处理:

1、让线程立即结束。(加上break)

2、让线程不结束,继续执行。(不加break)

3、让线程执行一些逻辑后,再结束。(写一些其他代码,再加break)

对于一个服务器程序来说,稳定性很重要,这些所谓的“问题”,在java中就会以异常的形式体现出来,可以通过catch语句,对这些异常进行处理。主要的几种处理方式:

1、尝试自动恢复

2、记录日志

3、发出警报

8、等待线程(结束),

join( )。我们知道,多线程的执行顺序是不同的(随机调度,抢占式执行),有不可预期性,虽然线程的调度是无序的,但是我们可以调用一些api,来影响带线程的执行顺序,join就是一种方式,影响 线程结束 的先后顺序。比如:t2 线程等待 t1,这时,一定是 t1先结束,t2 后结束。这里的join就会使 t2 线程阻塞。

代码:

public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
           for (int i = 0; i < 5; i++) {
               System.out.println("我是一个线程,正在工作");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
            System.out.println("线程结束");
        });
        
        t.start();
        //t.join();
        
        System.out.println("这时一个主线程,在t线程结束后再执行");
    }
}

我们想让t线程执行完后再执行主线程,如果不写join方法。因为多线程的调度是随机性的,执行效果如下:

执行效果和我们预期的不一样,加上join方法后,

代码:

public class ThreadDemo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
           for (int i = 0; i < 5; i++) {
               System.out.println("我是一个线程,正在工作");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
            System.out.println("线程结束");
        });
        t.start();
        t.join();
        System.out.println("这时一个主线程,在t线程结束后再执行");
    }
}

执行效果:

结果和我们想要预期效果相同。这里的join的意思是:让 t 线程执行完,再执行本线程。

这里是让main线程主动放弃去调度器调度,t 线程执行完后,main线程才执行,这也代表哪个线程调用join,哪个线程就是阻塞等待。这里就有了先后顺序。

注意:这里的先后顺序和优先级不同,优先级是系统调度器,在内核中完成的工作,即使优先级有差异,但是每个线程的执行顺序还是随机的(控制不了)。

等待线程结束还有两个重载方法,里面放的是要等待的时间,如图:

如果join不加参数,就是死等,第一个加参数的就是:等待多少毫秒,第二个加参数:数值精确到纳秒,不过很少用,因为计算机精确不到这么小。加参数是带有时间的等。

注意,有时候不想等,我们也可以不等,例如加 interrupt,可以终止这个等待,我们也可以在idea看到join的实现,是throws了 InterruptedException的。

9、获取线程的引用,currentThread()

我们对于一个类继承Thread是可以通过this拿到线程的实例的,

代码如下:

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

效果如下:

但是,如果是接口或者我们使用匿名内部类 / lambda表达式,就不能获取到Thread的引用了,这时,Thread已经给我们提供了方法获取引用了:Thread.currentThread()。如下展示:

代码:

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

效果如下:


都看到这了,点个赞再走吧,谢谢谢谢谢!!!

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

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

相关文章

ubuntu22.04 arrch64版在线安装maven

脚本 if type -p mvn; thenecho "maven has been installed."elsecd /home/zenglgwget https://dlcdn.apache.org/maven/maven-3/3.9.5/binaries/apache-maven-3.9.5-bin.tar.gz --no-check-certificatetar vxf apache-maven-3.9.5-bin.tar.gz rm -rf /usr/local/mav…

Spark---SparkCore(一)

一、术语与宽窄依赖 1、术语解释 1、Master(standalone):资源管理的主节点&#xff08;进程&#xff09; 2、Cluster Manager:在集群上获取资源的外部服务(例如&#xff1a;standalone,Mesos,Yarn) 3、Worker Node(standalone):资源管理的从节点(进程)或者说管理本机资源的…

js实现数组的上下移动

思路&#xff1a;上移表示index索引位置减去1&#xff0c;下移表示index索引位置增加1。使用数组的splice方法实现。例如上移&#xff1a;splice(元素当前索引位置&#xff0c;1(删除1个元素)&#xff0c;‘元素当前索引位置 - 1’)。

Unity 引擎宣布:自 2024 年起,开发者需支付费用!

Unity引擎宣布的新的收费模式&#xff0c;从2024年1月1日开始&#xff0c;根据游戏的安装量来对开发者进行收费。具体来说&#xff0c;每次游戏被下载时&#xff0c;UnityRuntime也会被安装&#xff0c;因此可能会产生额外的费用。对于开发者来说&#xff0c;需要注意以下几点&…

Linux如何查找某个路径下大于1G的文件

find 命令可以用于在 Linux 或 macOS 系统中查找文件和目录。如果你想查找大于1GB的文件&#xff0c;可以使用 -size 选项结合 参数。以下是一个示例&#xff1a; find /path/to/search -type f -size 1G这里的 /path/to/search 是你要搜索的目录的路径。这个命令将查找该目录…

“java.lang.IllegalStateException: No ConfigurableListableBeanFactory set“,缺少配置

一、错误分析 做品优购项目的运营商安全登录时&#xff0c;运行项目后&#xff0c;浏览器访问模板页&#xff0c;模板页的表格无法正常显示&#xff0c;报错信息如下&#xff1a; SEVERE: StandardWrapper.Throwable java.lang.IllegalStateException: No ConfigurableLista…

让工作效率提升10倍:十大AIGC工具评测【建议收藏】

AI技术的普及已经在近年来不断增长。这种技术已经改变了我们与电脑的互动方式&#xff0c;让我们能够更高效、更自然地完成任务。本文将展示10个基于ChatGPT、GPT-3.5和 GPT-4.0 AI模型构建的最强大的资源&#xff0c;使您更容易充分利用它们的潜力。因此&#xff0c;如果您想利…

[HOW TO]-VirtualBox的虚拟机通过宿主机上网

快速链接: . &#x1f449;&#x1f449;&#x1f449; [专栏目录]-环境搭建安装问题笔记目录 &#x1f448;&#x1f448;&#x1f448; 付费专栏-付费课程 【购买须知】:&#x1f449;&#x1f449;&#x1f449; 个人博客笔记导读目录(全部) &#x1f448;&#x1f448;&a…

AI制作的《大多数普通女孩的一生》——公开教程和工作流

内容来源&#xff1a;JiamigouCn ​这周由AI制作的《大多数普通女孩的一生》&#xff0c;在抖音爆火&#xff0c;获得新华网转发。到目前为止&#xff0c;全网还没有公开教程和工作流&#xff0c;需要花费800-2000购买。 本着AI社区共享原则&#xff0c;我委托公众号“楚思智能…

当内容创作进入 AGI 时代,你也可以成为「神笔马良」

我神笔马良的童话故事我们或多或少都听过&#xff0c;一支神笔在手&#xff0c;想画什么就能画出什么&#xff0c;栩栩如生。创造者的理解力、想象力和创作力都能通过这支神笔释放。 近一年&#xff0c;随着 AIGC 内容生产工具的快速出圈&#xff0c;有人把 Stable Diffusion、…

【T3】畅捷通T3软件数据恢复提示:为备份或还原操作指定的设备太多;只允许指定64个设备。

【问题描述】 畅捷通T3软件还原账套/数据恢复过程中提示&#xff1a; SQL Server为备份或还原操作指定的设备太多&#xff1b;只允许指定64个设备。 【解决方法】 提示这个错误时&#xff0c; 说明是在把从SQLServer2005上备份的数据还原到SQLServer2000数据库上。 即&#x…

Nginx 配置错误导致的漏洞

目录 1. CRLF注入漏洞 Bottle HTTP头注入漏洞 2.目录穿越漏洞 3. http add_header被覆盖 本篇要复现的漏洞实验有一个网站直接为我们提供了Docker的环境&#xff0c;我们只需要下载下来就可以使用&#xff1a; Docker环境的安装可以参考&#xff1a;Docker安装 漏洞环境的…

Sublime Text 4168最新代码编辑

Sublime Text是一款功能强大的文本编辑器&#xff0c;具有以下主要功能&#xff1a; 支持多种编程语言的语法高亮和代码自动完成功能&#xff0c;包括Python、JavaScript、HTML、CSS等。提供代码片段&#xff08;Snippet&#xff09;功能&#xff0c;可以将常用的代码片段保存…

vite项目配置vite.config.ts在打包过程中去除日志

在生产环境上&#xff0c;务必要将日志清除干净&#xff0c;其因有二&#xff0c;在webgis系统中&#xff0c;有很多几何数据&#xff0c;体积大、数量多&#xff0c;很容易引起系统卡顿&#xff1b;清除log后&#xff0c;系统看着舒服&#xff0c;协同开发有很多无聊的日志&am…

展现天津援疆工作成果 “团结村里看振兴”媒体采风团走进和田

央广网天津11月19日消息(记者周思杨)11月18日&#xff0c;由媒体记者、书法和摄影家、旅行社企业代表等40余人组成的“团结村里看振兴”媒体采风团走进新疆和田。在接下来的一周时间里&#xff0c;采风团将走访天津援疆和田地区策勒县、于田县、民丰县乡村振兴示范村&#xff0…

FL Studio21.2.0中文语言包编曲软件入门讲解

FL Studio常称水果&#xff0c;是一款功能强大的编曲软件&#xff0c;集编曲&#xff0c;录音&#xff0c;剪辑&#xff0c;混音于一身&#xff0c;简单易上手&#xff0c;灵活性高&#xff0c;强大到突破想象。 FL Studio&#xff0c;当前版本 FL Studio21&#xff0c;百分之…

PyQt基础_004_ 按钮类控件QPushButton以及自定义按钮控件

Qpushbutton基本操作 1.热键 2.按钮加图标 3.按钮事件等 import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import *class Form(QDialog):def __init__(self, parentNone):super(Form, self).__init__(parent)layout QVBoxLayout()se…

基于Vue+SpringBoot的个人健康管理系统

项目编号&#xff1a; S 040 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S040&#xff0c;文末获取源码。} 项目编号&#xff1a;S040&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 健康档案模块2.2 体检档案模块2.3 健…

Linux 6.7全面改进x86 CPU微码加载方式

导读最近&#xff0c;社区在清理 Linux 上的 Intel/AMD x86 CPU 微代码加载方面做了大量的工作&#xff0c;这些工作现已合并到 Linux 6.7 中。 由于在启动时加载 CPU 微代码对于减少不断出现的新 CPU 安全漏洞以及有时解决功能问题非常重要&#xff0c;Thomas Gleixner 最近开…

什么是 TLS/SSL 握手

TLS/SSL 握手是一个加密过程&#xff0c;每当客户端&#xff08;如浏览器&#xff09;与服务器建立连接时&#xff0c;都会在后台进行&#xff0c;此握手协议有助于客户端和服务器之间的安全连接&#xff0c;从而促进隐私、数据完整性和机密性。 TLS/SSL 握手何时发生 每当客…