✨哈喽,进来的小伙伴们,你们好耶!✨
🛰️🛰️系列专栏:【JavaEE】
✈️✈️本篇内容:Thread类再剖析!
🚀🚀代码存放仓库gitee:JavaEE初阶代码存放!
⛵⛵作者简介:一名双非本科大三在读的科班Java编程小白,道阻且长,星夜启程!
目录
一、Thread(String name)
二、是否后台线程 isDeamon()
三、是否存活 isAlive()
四、run()方法和start()方法的区别
五、中断线程
法一:手动设置一个标志位。
法二:使用Thread中内置的标志位来判定。
六、线程等待join()
七、线程休眠sleep()
一、Thread(String name)
定义:这个东西是给线程(thread 对象) 起一个名字。起一个啥样的名字,不影响线程本身的执行,仅仅只是影响到程序猿调试可以借助一些工具看到每个线程以及名字.很容易在调试中对线程做出区分。
那么如何查看线程的名字呢?这里jdk本身就自带了一个程序叫做jconsole可以用来查看本地进程。
那么首先先在我们自己idea里面创建一个线程demo8,右键运行,代码如下:
public class demo8 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true){
System.out.println("hello 1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"T1");
t1.start();
Thread t2 = new Thread(() -> {
while (true){
System.out.println("hello 2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"T2");
t2.start();
}
}
运行结果:
查看线程步骤如下:
1、首先找到我们的电脑安装jdk的位置,这里给大家展示一下博主本人的电脑jdk路径下的jconsole位置。
2、 ok,那么找到这个jconsole程序我们直接双击打开就可以查看我们电脑的本地进程:
jconsole 这里能够罗列出你系统上的 java 进程,上图红色圈起来的部分从上往下依次对应的线程是自己在idea里面创建的线程demo8,IDEA,jconsole本身。
3、双击我们自己创建的线程demo8,可以看到是下面这张图片的样式,因为我们电脑自带防火墙系统,所以直接点击不安全的连接即可。
4、进来之后我们点击线程这一列,就可以看到我们创建的线程t1,t2。
可以看到,当我们的java线程一启动,这里面还有一些其他的线程,这些显示是jvm自己创建的用来处理一些其他的任务。
二、是否后台线程 isDeamon()
什么是后台线程呢?
如果线程是后台线程,就不影响 进程退出如果线程不是后台线程(前台线程),就会影响到进程退出。
创建的 t1 和 t2 默认都是前台的线程。
即使 main 方法执行完毕,进程也不能退出.得等 t1 和 t2 都执行完,整个进程才能退出!!!如果 t1 和 t2 是后台线程此时如果 main 执行完毕,整个进程就直接退出,t1 和 t2 就被强行终止了。
三、是否存活 isAlive()
这个属性的意思就是——操作系统中对应的线程是否正在运行。
Thread t 对象的生命周期和内核中对应的线程,生命周期并不完全一致~~创建出 t 对象之后,在调用 start 之前,系统中是没有对应线程的~~在 run 方法执行完了之后, 系统中的线程就销毁了但是 t 这个对象可能还存在。通过 isAlive 就能判定当前系统的线程的运行情况。
如果 调用 start 之后, run 执行完之前,isAlive 就是返回 true
如果 调用 start 之前, run 执行完之后, isAlive 就返回 false
四、run()方法和start()方法的区别
run 单纯的只是一个普通的方法, 描述了任务的内容。
start 则是一个特殊的方法, 内部会在系统中创建线程。
看一段代码:
run 方法只是一个普通的方法你在 main 线程里调用 run,其实并没有创建新的线程这个循环仍然是在 main 线程中执行。
五、中断线程
中断线程的意思就是让一个线程停下来!
线程停下来的关键,是要让线程对应的 run 方法执行完(还有一个特殊的,是 main 这个线程对于 main 来说,得是 main 方法执行完,线程就完了)。
法一:手动设置一个标志位。
看一段代码:我们在这个代码里面创建一个标志位isquit,通过while(!isquit)来判断循环是否结束,可以通过手动的设置isquit的值来控制线程的结束与否。为什么在其他线程中修改isquit的值可以影响到当前线程呢?此处因为,多个线程共用同一个虚拟地址空间!!因此, main 线程修改的 isquit 和 t 线程判定的 isquit, 是同一个值。
public class demo10 {
public static boolean isquit = false;
public static void main(String[] args) {
Thread t = new Thread(() -> {
while(!isquit){
System.out.println("hello Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//只要把这个isquit设置为true,进一步的run就执行完了,再进一步就是线程执行结束了。
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isquit = true;
System.out.println("终止线程!");
}
}
那么像刚才那种写法并不够严谨,我们可以参考第二种写法。
法二:使用Thread中内置的标志位来判定。
我们可以通过:
Thread.interrupted()—— 一个静态的方法。
Thread.currentThread0.islnterrupted()—— 实例方法.
其中 currentThread 能够获取到当前线程的实例。
看新的代码段:
public class demo11 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()){
System.out.println("hello thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//当触发异常之后 立刻退出循环
break;
}
}
});
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
}
那么使用这种方法的原因就是——这个代码绝大部分情况,都是在休眠状态阻塞,此处的中断,是希望能够立即产生效果。如果线程已经是阻塞状态下,此时设置标志位就不能起到及时唤醒的效果
异常处理方式:调用这个 interrupt 方法, 就会让 sleep 触发一个异常从而导致 线程从阻塞状态被唤醒。
当下的代码,一旦触发了异常之后, 就进入了 catch 语句.在catch中,就单纯的只是打了一个日志
printStackTrace 是打印当前出现异常位置的代码调用栈,打完日志之后,就直接继续运行。
我们可以发现在代码的最后我们调用了interrupt()方法,那么调用这个方法可能产生两种情况。
1、如果t线程处在就绪状态,就是设置线程的标志位为true
2、如果t线程处在阻塞状态,就会触发一个InterruptException。
Thread.currentThread0.islnterrupted()—— 实例方法.
这个方法判定的标志位是 Thread 的普通成员,每个示例都有自己的标志位,所以一般无脑使用这个方法即可。
六、线程等待join()
在我们的日常开发中,因为线程之间的调度顺序是按照调度器来安排的,这个过程是随机,无序的,我们希望能够控制多个线程之间的调度顺序,那么线程等待就是一种方式。
要想实现线程等待这里会使用到一个函数叫做join(),即哪个线程调用join哪个线程就会进入阻塞状态。
代码演示:
public class demo12 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
for (int i = 0; i < 5; i++) {
System.out.println("hello thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//在主线程中就可以使用一个等待操作,来等待t线程执行结束。
try {
t.join(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我们可以看到在代码的后面出现了这么一行代码 t.join(10000);这里的意思就是让main线程等待t线程的run方法执行完毕,等待时间是10000ms。
七、线程休眠sleep()
这个线程休眠这里就不在过多赘述了,因为之前的案例中已经出现了很多的实例调用了sleep()方法。这里介绍一下进程中各个线程之间的关系。
进程是由PCB + 双向链表组成。这个说法是针对只有一个线程的进程,是如此的。
如果是一个进程有多个线程, 此时每个线程都有一个 PCB一个进程对应的就是一组 PCB 了。PCB 上有一个字段 tgroupld,这个 id 其实就相当于进程的 id.同一个进程中的若干个线程的 tgroupld 是相同的。
即某个线程调用了sleep()方法,PCB就会进入阻塞队列,操作系统调度线程的时候,就只是从就绪队列中挑选合适的 PCB到 CPU 上运行。阻塞队列里的 PCB 就只能干等着。