Thread 类的基本方法
创始人
2025-05-28 14:08:35
0

目录

  • 线程创建(五种方法)
    • 1. 继承 Thread, 重写 run
    • 2. 实现 Runnable 接口, 重写 run
    • 3. 继承 Thread, 重写 run, 使用匿名内部类
    • 4. 实现 Runnable, 重写 run, 使用匿名内部类
    • 5. 使用 lambda 表达式(==重点掌握==)
  • 线程休眠
  • 获取线程实例
  • 线程中断
    • 1. 给线程中设定一个结束标志位
    • 2. 使用Thread类内置的标志位来控制
  • 线程等待

线程创建(五种方法)

run方法是线程的入口方法, 每次我们去创建线程时都要重写run方法, run方法执行结束即该线程结束.

1. 继承 Thread, 重写 run

class MyThread extends Thread {@Overridepublic void run() {  System.out.println("run");}
}public class ThreadDemo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();System.out.println("main");}
}

输出: main run 或 run main
因为线程调度不同, 所以线程执行快慢不一样.

2. 实现 Runnable 接口, 重写 run

class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("run");}
}public class ThreadDemo2 {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread t = new Thread(myRunnable);t.start();System.out.println("main");}
}

3. 继承 Thread, 重写 run, 使用匿名内部类

public class ThreadDemo3 {public static void main(String[] args) {Thread t = new MyThread() {@Overridepublic void run() {System.out.println("run");}};t.start();System.out.println("main");}
}

4. 实现 Runnable, 重写 run, 使用匿名内部类

public class ThreadDemo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("run");}});t.start();System.out.println("main");}
}

5. 使用 lambda 表达式(重点掌握)

public class ThreadDemo5 {public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println("run");});t.start();System.out.println("main");}
}

线程休眠

class MyThread extends Thread {@Overridepublic void run() {while(true) {System.out.println("run");try {   //因为sleep是静态方法,所以可以直接调用Thread.sleep(1000);  //可能会出现异常, 所以要捕获} catch (InterruptedException e) {e.printStackTrace();}}}
}public class ThreadDemo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();while(true) {System.out.println("main");try {Thread.sleep(1000);  //单位是毫秒, 这里是1s} catch (InterruptedException e) {e.printStackTrace();}}}
}

输出:在这里插入图片描述
因为线程调度不同, 所以这里打印是随机的, 这里没有体现出来.
因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的.

获取线程实例

Thread.currentThread() 获取线程的实例

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

输出: main

线程中断

就是让一个线程停下来, 线程终止.
本质上来说, 让一个线程终止就一个方法, 让线程的入口方法执行完.

1. 给线程中设定一个结束标志位

class MyThread extends Thread {public static boolean isQuit;  //设置一个成员变量, 表示标志位@Overridepublic void run() {while(!isQuit) {  //通过成员变量我们可以控制while循环System.out.println("run");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("程序终止!");}public static void main(String[] args) {Thread t = new MyThread();t.start();System.out.println("main");isQuit = true;  //在主线程中将标志位置为true, 影响到t线程}
}

上述代码中, 我们用的是成员变量设置标志位, 那局部变量可以吗?
我们可以试试:

在这里插入图片描述
显示找不到该变量, 为什么找不到呢?
按理来说线程与线程之间共用一个内存地址空间, 它们之间的变量都可以访问的, 那为什么不行呢?
其实这里和 lambda 表达式有关.

lambda 表达式中捕获的变量必须是 final 修饰的 或者是 “实际 final”(没有被final修饰, 但是代码中没有对该变量进行修改过)

再看上述代码, isQuit 在主线程中创建, 又被修改过, 所以不行.

2. 使用Thread类内置的标志位来控制

class MyThread extends Thread {@Overridepublic void run() {//这里的Thread.currentThread()是获取当前对象的实例,也就是t//isInterrupted()得到内置标志位, 初始为falsewhile(!Thread.currentThread().isInterrupted()) {System.out.println("run");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("程序终止!");}public static void main(String[] args) {Thread t = new MyThread();t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}//interrupt() 设置标志位为truet.interrupt();}
}

在这里插入图片描述

当我们在调用 interrupt() 时, 如果该线程在正在阻塞中(比如正在sleep中), 此时就会把阻塞状态唤醒, 通过抛出异常的方式让sleep立即结束.
注意:
当线程处于sleep状态被唤醒时, sleep会自动把isInterrupted标志位清空(true -> false). 这就导致下次循环还是会进去, 而主线程已近结束了, 没有interrupt() 来影响标志位, 就会一直循环打印run.

这就产生了上面的输出结果.
当然, 如果sleep刚刚好结束了, 这时候我们调用interrupt(), 则直接结束循环.(这种情况是很低的)
我们可以通过添加一个break来让代码抛出异常后退出循环.

class MyThread extends Thread {@Overridepublic void run() {while(!Thread.currentThread().isInterrupted()) {System.out.println("run");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();break;}}System.out.println("程序终止!");}public static void main(String[] args) {Thread t = new MyThread();t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}t.interrupt();}
}

在这里插入图片描述

线程等待

因为线程之间是并发执行的, 操作系统对线程的调度是无序的, 所以我们无法判断哪个线程先结束, 哪个后结束.
因此, 在Thread类里提供了一个 join 方法, 来进行线程间的等待.
比如:
t.join() 放在 main 线程里就是main线程等待 t 线程结束. 这时main线程是阻塞状态, 直到 t 执行结束main线程才会从阻塞解除, 继续执行.
例:

public class ThreadDemo5 {     //使用join方法可能会有异常,所以声明一下public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {System.out.println("t1");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}});Thread t2 = new Thread(() -> {try {t1.join();   //放在这里就是t2等待t1结束} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t2");});//首先创建两个线程t1.start();t2.start();t2.join();   //这里main线程等待t2结束System.out.println("main");}
}

在这里插入图片描述
首先main线程等待 t2 线程执行结束, 而 t2 线程在等待 t1 线程结束, 所以就出现上面结果.

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...