【JavaEE初阶】Thread类及常见方法

目录

📕 Thread类的概念

📕 Thread 的常见构造方法

📕 Thread 的几个常见属性

📕 start()-启动一个线程

📕 中断一个线程

🚩 实例一

🚩 实例二

🚩 实例三

📕 join()-等待一个线程

🚩获取当前线程引用

🚩休眠当前线程


📕 Thread类的概念

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。

每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理

📕 Thread 的常见构造方法

前面两个构造方法上述在前面已经讲过,第三个和第四个构造方法是可以在创建线程的时候,给线程起个名字,是否起名字对于线程运行的效果并没有影响,好处是Java运行过程中,可以看到每个线程的名字,出问题的时候更直观的把出现的问题和代码联系起来(方便调试),通过jconsole / IDEA调试器都能看到线程名字,若没起名字,也有默认的名字Thread-0,Thread-1,Thread-2....

取名:

📕 Thread 的几个常见属性

常见属性说明:

  • ID 是线程的唯一标识,不同线程不会重复,这里的id和pcb的id是不同的,是jvm自己搞的一套体系,Java代码也无法获取到pcb的id

  • 名称是各种调试工具用到

  • 状态表示线程当前所处的一个情况,后面会一一介绍

  • 优先级高的线程理论上来说更容易被调度到,但是虽然Java提供了优先级接口,实际上修改了优先级接口现象也不是很明显,你修改的优先级是一回事,系统调度又是另一回事,你这里的优先级只能是一个"建议参考",具体还得以人家自身为准

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

例子:

将 t 线程设为后台线程:此时进程中只有main是前台进程了,只要main结束,整个进程就结束了,main执行完 start 立即结束,此时 t 线程还没有来得及打印,进程就结束了,里面的线程也自然随之结束了,所以什么也打印不出来。 

注意:此处也有一定的概率出现 t 打印一次,然后进程结束的情况,就是看main先执行结束还是t还执行一次(线程之间是抢占式执行,调度顺序不确定),但是按照经验来看,当前代码结构中,大概率是什么都不打印,即使你尝试运行1w次,结构可能都是什么都不打印,但是不能保证1w零1次是否打印!!!概率不均等的原因是main调用start的速度很快,对于 t 来说,要把线程创建出来之后才会执行打印,但是创建的本事有时间开销,虽然比进程的创建轻量,也不是为0,相对于main来说要更慢!!!

  • 是否存活,即简单的理解,为 run 方法是否运行结束了

指的是系统中的线程(pcb)是否还存在,Thread对象的声命周期和pcb的生命周期不一定完全一样

例子:

更改:

正因为Thread类和系统中的线程生命周期不一致,因此就可以通过上述谈到的 isAlive 方法,判定系统中的线程是否仍然存在

判断出当前这个线程是否存货(指的是内核中的线程,与对象本身没有关系),此时现在进程已经结束了,得到的结果为false。

当我们把线程中睡眠加上,因为线程中是sleep(2000),下面是sleep(1000),进行打印的时候try—>catch中的还没有睡醒,即线程还存在,所以结果为true。

  • 线程的中断问题,下面会进一步说明

上述属性中名称,后台线程,是否存货了解的重点!!!

注意,上述属性一定是写在 start 之前的!!!

📕 start()-启动一个线程

start 才是正在的创建线程(在内核中创建pcb),一个线程需要通过run/lambda把线程要执行的任务定义出来,start 才是正在的创建线程,并开始执行

核心就是是否真正的创建线程出来,每个线程都是独立调度执行的(相当于整个程序中多了一个执行流,即多了一个while死循环,并发执行)

一个 Thread 对象只能 start 一次

要想在搞一个新的线程,就需要要创建另一个 Thread 类对象

📕 中断一个线程

与其叫中断,不如说终止,中断整个词在计算机中包含很多意思。

🚩 实例一

1,自己实现,控制线程结束的代码例子

我们不在while中写true,自己创建一个boolean类型的变量来作为while的条件,让其3s之后,修改变量的值来结束 t 线程,这样让main线程去决定 t 线程结束这样的效果,所谓让 t 线程结束,就是让线程的入口方法执行完毕,对于while来说就是结束循环即可

上述代码,我们是把isRunning定义成的一个成员变量,若把他定义成一个局部变量,代码就会直接编译报错,

如果上述代码 t 线程是睡眠10s,甚至更长,此时的main线程是无法及时的把 t 线程终止掉,就有 Thread 类提供的方法来实现。

🚩 实例二

2,使用 Thread 类提供的interrupt方法和 isInterruptted方法,来实现上述效果

Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记

刚才是自己定义了一个boolean变量,实际上Thread里面内置了一个,使用内置的标志位,功能更强大!!!

🚩 实例三

观察标志位是否清除

所以可以认为interrupt方法,线程中有sleep,就会唤醒sleep,若代码没有执行到sleep,就还是一个单纯的设置标志位。

📕 join()-等待一个线程

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。

例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束

例子:

由于线程是"抢占式"执行且并发执行,所以谁先结束每次都是不确定的,如果希望让代码里面的 t 先结束,main后结束,就可以在main中使用线程等待(join)。

可以认为谁调用jion谁就阻塞,比如代码中有main线程,又有t1和t2线程,此时main调用t1.join,mian就阻塞,然后t1和t2并发执行,如果t1打印比t2打印的时间短,那么此时t1和t2一起打印,当t1打印完之后,t2还没有打印完,main线程已经回到就绪态,那么t2就和main一起打印。

在main线程中调用两个join:

t1和t2之间相互等待:

此时的代码就是t1等待t2,main线程又在等待t1

还可以其他线程等待main:(非常规写法)

上述只是join无参数版本的,也就是死等,只要 t 不结束,就会一直等待下去,还要带参数的版本

在实际开发中,一般很少使用死等这个策略

传入一个时间:传入的时间是最大等待时间,比如写的等待10s,如果10s之内,t 线程结束了之间返回,如果10s到了,t 线程还没有结束不等了!!!继续往下走。(第三种方法纳秒级别的时间,对于主流系统来说,更精细了会导致误差)

🚩获取当前线程引用

这个方法我们以及非常熟悉了,前面都已经使用过了,就不做过多赘述了

🚩休眠当前线程

也是我们比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。

sleep方法是native修饰的,底层是C++代码写的

  • 14
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值