java EE初阶 — Thread类及常见方法

news2024/11/29 6:32:23

文章目录

    • 1.Thread 常见的构造方法
    • 2.Thread 几个常见的属性
    • 3.启动一个线程 - start()
    • 4.终止一个线程
      • 4.1 使用标志位来控制线程是否要停止
      • 4.2 使用 Thread 自带的标志位来进行判定
    • 5.等待一个线程 - join()
    • 6.获取当前线程引用
    • 7.休眠当前线程

1.Thread 常见的构造方法

  • Thread() - 创建线程对象
  • Thread(Runnable target) - 使用 Runnable 对象创建线程对象
  • Thread(String name) - 创建线程对象,并且命名
  • Thread(Runnable trget, String name) - 使用 Runnable 对象创建线程对象,并且命名
  • Thread(ThreadGroup group, Runnable target) - 线程可以被用来分组管理,分号的即为线程组,目前了解即可

例子:

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("名字");
Thread t4 = new Thread(new MyRunnable(), "名字");
package thread;

public class ThreadDemo6 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello world");
                }
            }
        }, "MyRunnable");
        thread.start();
    }
}

2.Thread 几个常见的属性

1、 ID(getid()) 是线程的唯一标识,不同线程不会重复。


2、名称(getName()) 是构造方法里起的名字。


3、状态(getState()) 表示线程当前所处的一个情况,(java 里的线程状态要比操作系统原生的状态更丰富一些)
下面我们会进一步说明。


4、 优先级(getPriority)可以获取,也可以设置,但是没什么作用。


5、关于
后台线程(isDaemon())
,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。

前台线程会阻止进程结束,前台线程工作没做完,进程是是无法结束工作的。
后台线程不会阻止进程的结束,后台线程工作没做完,进程也是可以结束。

代码手动创建线程的时候。默认都是前台
包括 main 默认也是前台的,其他 JVM 自带的都是后台的。

也可以使用setDaemon设置后台线程,也是守护线程。

package thread;

public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, "MyRunnable");
        thread.setDaemon(true);
        thread.start();
    }
}

把 thread 设置成了后台线程/守护线程,此时进程是否结就与 thread 无关了。



6、是否存活(isAlive),即简单的理解,为 run 方法是否运行结束了

在这里插入图片描述
如果光是创建一个 thread 变量,不调用 start 则在系统内核里不会有线程。

创建变量就相当于是把一个任务梳理好了,而调用 start 就相当于是开始做任务。

在真正调用 start 之前,调用 thread.isAlive ,就是false。
调用 start 之后,isAlive 就是 true。

例子:

package thread;

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello java");
            }
        }, "Runnable");
        thread.start();
        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println(thread.isAlive());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


isAlive 是在判断当前系统里面的这个线程是不是真的存在了。

run 执行完了,内核里的PCB就释放了。
操作系统里的线程就没了。
但是 thread 这个对象还在,当引用不指向这个对象没被GC回收时,thread 就不存在了。

总结:

  • 如果 thread 的 run 还没跑,isAlive 就是 false。
  • 如果 thread 的 run 正在跑,isAlive 就是 true。
  • 如果 thread 的 run 跑完了,isAlive 就是 false。


isAlive为true的例子: run 里面的线程会在执行3秒以后销毁。

package thread;

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++)  try {
                    System.out.println("hello java");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Runnable");
        thread.start();
        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println(thread.isAlive());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


因为抢占式执行,hello java 在前还是 true 在前是不确定的。
要看调度器结果,这是不可预期的。


7、线程的中断问题(interrupted()),下面我们进一步说明。

3.启动一个线程 - start()

线程对象被创建出来并不意味着线程就开始运行了。

调用 start 方法, 才真的在操作系统的底层创建出一个线程。

就像是前面说的,创建对象相当于是梳理任务,而调用 start 则是开始执行任务。

4.终止一个线程

终止意思不是让线程立即就停止,而是通知线程应该要停止了。
但是是否真的停止,取决于线程这里具体的代码的写法。

4.1 使用标志位来控制线程是否要停止

package thread;

public class ThreadDemo9 {
    public static boolean flag = true;

    public static void main(String[] args) throws InterruptedException{
        Thread thread = new Thread(() -> {
            while (flag) {
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();

        Thread.sleep(3000);//3秒后结束线程
        //主线程这里可以随时通过flag变量的取值,来操作 thread 线程是否结束。
        flag = false;
    }
}

这段代码执行后3秒钟会结束线程。


自定义变量这种方式,不能及时响应。
尤其是在 sleep 休眠时间比较久的时候。

thread 线程之所以会结束,完全取决于 thread 线程内部的代码的 flag。
通过 flag 来控制循环。

这里只是告知线程要结束了,但是什么时候结束,都是取决于线程内部的代码是如何实现的

4.2 使用 Thread 自带的标志位来进行判定

package thread;

public class ThreadDemo10 {
    public static void main(String[] args) throws InterruptedException{
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello juejin");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(3000);
        thread.interrupt();
    }
}

currentThread() 是Thread 类的静态方法
通过这个方法可以获取到当前线程。
哪个线程调用的这个方法,就是得到哪个线程的对象引用。

isInterrupted() 是在 thread.run中被调用的。
此处获取的线程就是 thread 线程。
为 true 表示被终止,为 false 表示未被终止。
这个方法的背后,就相当于是判断定一个 boolean 变量。

interrupt() 是用来终止 thread 线程的。
相当于是在设置这个 boolean 变量。

如果线程在 sleep 中休眠,此时调用 interrupt会把线程唤醒。从 sleep 提前返回了。
interrupt 会触发 sleep 内部的异常,导致 sleep 提前返回。


可以看到在3秒钟执行结束后,抛了一个异常后又继续执行了。

这是因为 interrupt会做两件事:

  • 把线程内部的标志位(boolean)给设置成true。
  • 如果线程在进行 sleep ,就会触发异常把 sleep 唤醒。
    但是 sleep 在唤醒的时候,还会做一件,就是把刚才设置的这个标志位再设置为 false 。(情况标志位)
    这就导致了当 sleep 的异常被 catch 完之后,循环还要继续执行。


当然也可以在唤醒 sleep 之后就停止,在刚才的代码中加入一个 break 即可。


可以看到抛丸异常就停止了。

怎样终止线程、什么时候终止,主要还是看代码是怎样实现的。

5.等待一个线程 - join()

线程是一个随机调度的过程。
等待线程做的事情,就是在控制两个线程的结束顺序。

package thread;

public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
             for (int i = 0; i < 3; i++) {
                 System.out.println("hello thread");
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                     break;
                 }
             }
        });
        thread.start();
        System.out.println("join之前");

        //此处的join就是让当前的main线程来等待thread线程指向结束(等待thread的run执行完)
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join 之后");
    }
}

本身执行完 start 之后,thread 线程和 main 线程就并发执行分头行动。
main 继续往下执行,thread 也会继续往下执行。

join() 使线程发生阻塞,会一直阻塞到 thread 线程结束时,
main 线程才会从join中恢复过来,才能继续执行下去,因此 thread 线程肯定是比 main 先结束的。



main 线程等待 thread 执行结束后才会执行。

如果执行 join 时,thread 已经结束了,join 不会阻塞,就会立即返回。



join 还有另一种用法:

public void join(long millis)

此方法的含义是,指定等待的时间。
这种方式的操作比较常见。

上面的无参数的 join() 则会一直等待。

6.获取当前线程引用

public static Thread currentThread(); //返回当前线程对象的引用

在哪个线程调用,就能获取到哪个线程的实例。

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

7.休眠当前线程

//休眠当前线程 millis毫秒
public static void sleep(long millis) throws InterruptedException 
//可以更高精度的休眠
public static void sleep(long millis, int nanos) throws InterruptedException 

让线程休眠,本质上就是让这个线程不参与调度了。(不去CPU上执行了)


在操作系统里面有一个就绪队列和一个阻塞队列


操作系统每次需要调度一个线程去执行,就从就队列中选一个就好了。

PCB 是使用链表来组织的。(并不具体)
实际的情况并不是一个简单的链表,其实这是个一系列以链表为核心的数据结构。

线程 A 调用 sleep ,A 就会进入休眠状态。
把 A 从上述链表中拿出来,放到另一个链表中。


另一个链表里的PCB都是阻塞状态,暂时不参与 CPU 调度执行,是阻塞队列。

一旦线程进入阻塞状态,对应 PCB 就进入阻塞队列了,此时就暂时无法参与调度了。

比如调用 sleep(1000) ,对应的线程 PCB 就要在阻塞队列中待1000ms这么长的时间。


当这个 PCB 回到就绪队列后并不会被立即执行。
因为虽然是 sleep(1000),但是实际上考虑到调度的开销,
对应的线程是无法在唤醒之前之后立即执行的,实际上的时间间隔大概率要大于 1000ms。

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

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

相关文章

【Spring】——11、了解BeanPostProcessor后置处理器

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

Packet Tracer - 综合技能练习(通过调整 OSPF 计时器来修改 OSPFv2 配置)

地址分配表 设备 接口 IP 地址 子网掩码 RA G0/0 192.168.1.1 255.255.255.0 RB G0/0 192.168.1.2 255.255.255.0 RC G0/0 192.168.1.3 255.255.255.0 S0/0/0 209.165.200.225 255.255.255.252 拓扑图 场景 在此综合技能练习中&#xff0c;您的重点是 OSPF…

【滤波跟踪】不变扩展卡尔曼滤波器对装有惯性导航系统和全球定位系统IMU+GPS进行滤波跟踪【含Matlab源码 2232期】

⛄一、简介 针对室内定位中的非视距&#xff08;Non-Line-of-Sight,NLOS&#xff09;现象,提出一个新型算法进行识别,同时有效缓解其影响.主要通过超宽带&#xff08;Ultra-Wideband,UWB&#xff09;定位系统与惯性导航系统&#xff08;Inertial Navigation System,INS&#x…

车辆大全和车牌识别系统毕业设计,车牌识别系统设计与实现,车牌AI识别系统论文毕设作品参考

功能清单 【后台管理员功能】 系统设置&#xff1a;设置网站简介、关于我们、联系我们、加入我们、法律声明 广告管理&#xff1a;设置小程序首页轮播图广告和链接 留言列表&#xff1a;所有用户留言信息列表&#xff0c;支持删除 会员列表&#xff1a;查看所有注册会员信息&a…

发布MagicOS 7.0, 荣耀如何打造“松弛感”的操作系统?

最近&#xff0c;“松弛感”一词特别流行。有博主教网友如何打造松弛感美女&#xff0c;因为这种毫不费力、天然去雕饰的美&#xff0c;更有吸引力&#xff1b;职场松弛感&#xff0c;能够平衡工作和生活&#xff0c;更被同事们喜欢&#xff1b;生活也需要多一些松弛感&#xf…

C. Doremy‘s City Construction(思维)

Problem - C - Codeforces Doremy的新城市正在建设中! 这个城市可以被看作是一个有n个顶点的简单无向图。第i个顶点的高度为ai。现在&#xff0c;多雷米正在决定哪些顶点对应该用边连接。 由于经济原因&#xff0c;图中不应该有自循环或多条边。 由于安全原因&#xff0c;不应…

mybatis基础01

一、安装mybatis 要使用 MyBatis&#xff0c; 只需将 mybatis-x.x.x.jar 文件置于类路径&#xff08;classpath&#xff09;中即可。 如果使用 Maven 来构建项目&#xff0c;则需将下面的依赖代码置于 pom.xml 文件中&#xff1a; <dependency><groupId>org.mybat…

贺利坚汇编语言课程笔记 绪论

贺利坚汇编语言课程笔记 绪论 又是女娲补天式地从零开始两周零基础冲击六十分… 文章目录贺利坚汇编语言课程笔记 绪论一.Why should we learn Assembly language&#xff1f;二.从机器语言到汇编语言三.计算机组成指令和数据的表示计算机中的存储单元计算机中的总线x86CPU性能…

Java日期时间的前世今生

&#x1f649; 作者简介&#xff1a; 全栈领域新星创作者 &#xff1b;天天被业务折腾得死去活来的同时依然保有对各项技术热忱的追求&#xff0c;把分享变成一种习惯&#xff0c;再小的帆也能远航。 &#x1f3e1; 个人主页&#xff1a;xiezhr的个人主页 前言 日常开发中&…

gitpod.io,云端开发调试工具。

gitpod&#xff0c;一款在线开发调试工具&#xff0c;使用它你可以在网页上直接开发软件项目了。 比如你的项目仓库在github上&#xff0c;你可以直接在网址的前面添加gitpod.io/#&#xff0c;然后回车就能在网页上使用vscode打开这个项目了。 打开的效果&#xff1a; 可以安装…

ZZULIOJ 2066: 带分数

ZZULIOJ 2066: 带分数 题意&#xff1a; 给定一个数NNN&#xff0c;问有多少组a,b,ca,b,ca,b,c满足abcNa\dfrac bcNacb​N&#xff0c;且a,b,ca,b,ca,b,c三个数不重不漏地涵盖1−91-91−9这999个数字&#xff0c;输出总组数 解题思路&#xff1a; 暴力枚举出999个数的全排列…

sql数据库入门(1)

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言初阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>: 本篇记录一下牛牛在学校学习的sql serve数据库知识,内…

学了PS了还用学习AI吗,有什么区别

AdobeIllustrator和AdobePhotoshop它是目前市场上设计师使用最广泛的两种软件。很多刚接触的同学会发现&#xff0c;两者都可以达到一些效果&#xff0c;导致一种错觉&#xff0c;认为任何人都可以使用&#xff0c;所以他们可以随意使用。 虽然在PS和Ai它确实可以用来做类似的…

顶刊示例-经济研究数据-全国、省、市-城市人均收入、农村人均收入面板数据

&#xff08;1&#xff09;全国城乡居民人均收入 1、数据来源&#xff1a;中国统计年鉴 2、时间跨度&#xff1a;1978-2020 3、区域范围&#xff1a;国家 4、指标说明&#xff1a; 包含如下指标&#xff1a; 全国居民人均可支配收入 城镇居民人均可支配收入 农村居民人均…

深入浅出解析——MYSQL|触发器

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

AtCoder Beginner Contest 279 F BOX 并查集 (大意失荆州

前言 赛时一直RE&#xff0c;思路很清晰&#xff0c;不知道RE哪里。。qwq 赛后开断点发现&#xff0c;map的大小不变&#xff0c; 最后发现是一个if条件写错了&#xff0c;寄。 不知道为什么会想起 银河英雄传说 题意&#xff1a; 初始n个盒子&#xff0c;盒子iii放着编号为i…

2023年天津财经大学珠江学院专升本退役士兵免试职业技能考查大纲

天津财经大学珠江学院2023年高职升本科职业技能综合考查考试大纲 &#xff08;仅适用于符合条件的退役士兵考生&#xff09;《管理学原理》 一、本大纲系天津财经大学珠江学院2023年高职升本科《管理学原理》职业技能综合考查考试大纲&#xff0c;仅适用于符合条件的退役士兵考…

面试官:synchronized与Lock有什么区别?

作为一名程序员&#xff0c;在求职面试时&#xff0c;不知道你在求职面试时常会遇到关于线程的问题。张工是一名java程序员&#xff0c;3年多工作经验&#xff0c;有次到一家互联网公司面试软件开发工程师岗位&#xff0c;面试官就问了他这样一个问题。synchronized与Lock有什么…

Android APP深度优化—内存映射机制(mmap)

mmap原理 open一个文件&#xff0c;然后调用mmap系统调用&#xff0c;将文件的内容的全部或一部分直接映射到进程虚拟空间中文件存储映射部分&#xff1b;完成映射关系后&#xff0c;mmap返回值是一个指针&#xff0c;进程可以通过采用指针方式读写操作这一段内存&#xff1b;…

vue3 antd项目实战——使用filter实现简单的table表格搜索功能

零基础filter实现最简单的table表格知识调用核心干货下期预告关键字模糊查找&#xff08;纯前端&#xff09;关键字模糊查找&#xff08;前后交互&#xff09;知识调用 功能实现可能要用到的知识&#xff1a;vue3ant design vuets实战【ant-design-vue组件库引入】vue3项目实战…