Java-Thread 类的基本用法(详细说明)

「爱情、让人受尽委屈。」 2023-09-27 16:51 255阅读 0赞

本篇博客,我将为大家介绍Thread类的基本用法,包括线程创建、中断、等待、休眠和获取线程实例等方面,希望对大家有一些帮助和参考

目录

一、线程的创建

1.1 继承Thread的方式去创建线程

1.2 通过实现Runnable接口创建线程

1.3 使用匿名内部类创建线程

1.4 使用lambda 表达式创建 Runnable 子类对象

1.5 start方法和run方法的区别

二、线程中断

三、线程等待

四、线程休眠

五、获取当前线程引用


一、线程的创建

1.1 继承Thread的方式去创建线程

1) 继承 Thread 来创建一个线程类.

  1. class MyThread extends Thread {
  2. @Override
  3. public void run() {
  4. System.out.println("这里是线程运行的代码");
  5. }
  6. }

2) 在main方法里创建 MyThread 类的实例

  1. MyThread t = new MyThread();

3) 调用 start 方法启动线程

  1. t.start(); // 线程开始运行

完整代码如下:

  1. public class MyThread extends Thread{
  2. @Override
  3. public void run() {
  4. System.out.println("t");
  5. }
  6. public static void main(String[] args) {
  7. MyThread t=new MyThread();
  8. t.start();//启动线程
  9. System.out.println("main");//主线程的打印
  10. }
  11. }

运行结果如下:

c43a15aa54714567b4d89c03d5742734.png

1.2 通过实现Runnable接口创建线程

1) 实现 Runnable 接口

  1. class MyRunnable implements Runnable {
  2. @Override
  3. public void run() {
  4. System.out.println("这里是线程运行的代码");
  5. }
  6. }

2) 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.

  1. Thread t = new Thread(new MyRunnable());

3) 调用 start 方法

  1. t.start(); // 线程开始运行

完整代码如下:

  1. class MyRunnable implements Runnable {
  2. @Override
  3. public void run() {
  4. System.out.println("这里是线程运行的代码");
  5. }
  6. public static void main(String[] args) {
  7. Thread t=new Thread(new MyRunnable());
  8. t.start();
  9. System.out.println("main");
  10. }
  11. }

运行结果如下:

2f98e99f583241c8b6e04a51858534d6.png

1.3 使用匿名内部类创建线程

这里将介绍俩种实现方式
以下是第一种方式,就是继承thead的方式去实现

  1. //使用匿名内部类实现继承thread
  2. public class ThreadDemo3 {
  3. public static void main(String[] args) {
  4. Thread t=new Thread(){
  5. @Override
  6. public void run(){
  7. while (true){
  8. System.out.println("t");
  9. try {
  10. Thread.sleep(1000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }
  16. };
  17. t.start();
  18. while (true){
  19. System.out.println("main");
  20. try {
  21. Thread.sleep(1000);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }
  27. }

另外一种是实现runable接口的方式去实现线程的创建

  1. //匿名内部类实现ThreadRunable接口
  2. public class MyThread {
  3. public static void main(String[] args) {
  4. Thread t=new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. System.out.println("t");
  8. try {
  9. Thread.sleep(1000);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. });
  15. t.start();
  16. while (true){
  17. System.out.println("main");
  18. try {
  19. Thread.sleep(1000);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25. }

1.4 使用lambda 表达式创建 Runnable 子类对象

  1. public class MyThread {
  2. public static void main(String[] args) {
  3. Thread t=new Thread(() ->{
  4. while (true){
  5. System.out.println("t");
  6. }
  7. });
  8. t.start();
  9. while (true) {
  10. System.out.println("main");
  11. }
  12. }

使用lambda表达式是为了使代码看起来更加简洁,跟其他创建线程方法功能一样,除此之外,工作中使用的最多的也是这种

运行结果如下:

93daee1459a94bfda85a1f95eaf30781.png

可能大家会好奇为什么不是依次交替执行的呢,本期疑问下期会给大家讲,先留一个悬念 :)

1.5 start方法和run方法的区别

下面来介绍一下,start方法和run方法的区别:
run() 方法只是线程的一个普通方法,调用它不会创建一个新的线程,而只是按顺序执行其中的代码。因此,如果直接调用 run() 方法,那么其中的代码会在当前线程中执行,不会开启新的线程。

start() 方法才是真正启动一个新线程并开始执行其中的 run() 方法。当调用 start() 方法时,Java 会创建一个新的线程,并在新的线程中执行 run() 方法中的代码。因此,如果要启动一个新线程并执行其中的代码,就必须使用 start() 方法。

二、线程中断

我们现来具体的描述一下 ,什么是线程中断,我们来描述一下:线程中断是指在多线程程序中,中断正在运行的线程的执行,简单来说就是让程序停下来,通常是由其他线程通过调用目标线程的interrupt()方法来实现。

我们来看下面一个例子:

李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们 需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如 何通知李四停止呢?这就涉及到我们的停止线程的方式了。

目前常见的有以下两种方式: 1. 通过共享的标记来进行沟通 2. 调用 interrupt() 方法来通知

案例1-使用自定义的变量来作为标志位.

需要给标志位上加 volatile 关键字,这个关键字的功能我们后面介绍

  1. public class ThreadDemo {
  2. private static class MyRunnable implements Runnable {
  3. public volatile boolean isQuit = false;
  4. @Override
  5. public void run() {
  6. while (!isQuit) {
  7. System.out.println(Thread.currentThread().getName()
  8. + ": 别管我,我忙着转账呢!");
  9. try {
  10. Thread.sleep(1000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. System.out.println(Thread.currentThread().getName()
  16. + ": 啊!险些误了大事");
  17. }
  18. }
  19. public static void main(String[] args) throws InterruptedException {
  20. MyRunnable target = new MyRunnable();
  21. Thread thread = new Thread(target, "李四");
  22. System.out.println(Thread.currentThread().getName()
  23. + ": 让李四开始转账。");
  24. thread.start();
  25. Thread.sleep(10 * 1000);
  26. System.out.println(Thread.currentThread().getName()
  27. + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
  28. target.isQuit = true;
  29. }
  30. }

案例2-使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.

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




















方法 说明
public void interrupt() 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知, 否则设置标志位
public static boolean interrupted() 判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean isInterrupted() 判断对象关联的线程的标志位是否设置,调用后不清除标志位

使用 thread 对象的 interrupted() 方法通知线程结束.

  1. public class ThreadDemo {
  2. private static class MyRunnable implements Runnable {
  3. @Override
  4. public void run() {
  5. // 两种方法均可以
  6. while (!Thread.interrupted()) {
  7. //while (!Thread.currentThread().isInterrupted()) {
  8. System.out.println(Thread.currentThread().getName()
  9. + ": 别管我,我忙着转账呢!");
  10. try {
  11. Thread.sleep(1000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. System.out.println(Thread.currentThread().getName()
  15. + ": 有内鬼,终止交易!");
  16. // 注意此处的 break
  17. break;
  18. }
  19. }
  20. System.out.println(Thread.currentThread().getName()
  21. + ": 啊!险些误了大事");
  22. }
  23. }
  24. public static void main(String[] args) throws InterruptedException {
  25. MyRunnable target = new MyRunnable();
  26. Thread thread = new Thread(target, "李四");
  27. System.out.println(Thread.currentThread().getName()
  28. + ": 让李四开始转账。");
  29. thread.start();
  30. Thread.sleep(10 * 1000);
  31. System.out.println(Thread.currentThread().getName()
  32. + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
  33. thread.interrupt();
  34. }
  35. }

thread 收到通知的方式有两种: 1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通 知,清除中断标志

附:当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择 忽略这个异常, 也可以跳出循环结束线程.

  1. 否则,只是内部的一个中断标志被设置,thread 可以通过Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志

或者通过Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志

这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。

案例3-观察标志位是否清除

使用 Thread.isInterrupted() , 线程中断会清除标志位.

  1. public class ThreadDemo {
  2. private static class MyRunnable implements Runnable {
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 10; i++) {
  6. System.out.println(Thread.interrupted());
  7. }
  8. }
  9. }
  10. public static void main(String[] args) throws InterruptedException {
  11. MyRunnable target = new MyRunnable();
  12. Thread thread = new Thread(target, "李四");
  13. thread.start();
  14. thread.interrupt();
  15. }
  16. }

运行结果如下:

  1. true // 只有一开始是 true,后边都是 false,因为标志位被清
  2. false
  3. false
  4. false
  5. false
  6. false
  7. false
  8. false
  9. false
  10. false

使用 Thread.currentThread().isInterrupted() , 线程中断标记位不会清除.

  1. public class ThreadDemo {
  2. private static class MyRunnable implements Runnable {
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 10; i++) {
  6. System.out.println(Thread.currentThread().isInterrupted());
  7. }
  8. }
  9. }
  10. public static void main(String[] args) throws InterruptedException {
  11. MyRunnable target = new MyRunnable();
  12. Thread thread = new Thread(target, "李四");
  13. thread.start();
  14. thread.interrupt();
  15. }
  16. }

运行结果如下:

  1. true // 全部是 true,因为标志位没有被清
  2. true
  3. true
  4. true
  5. true
  6. true
  7. true
  8. true
  9. true
  10. true

三、线程等待

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转 账成功,才决定是否存钱,这时我们可以使用join方法明确等待线程的结束。




















方法 说明
public void join() 等待线程结束
public void join(long millis) 等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos) 同理,但可以更高精度
  1. public class ThreadDemo {
  2. public static void main(String[] args) throws InterruptedException {
  3. Runnable target = () -> {
  4. for (int i = 0; i < 5; i++) {
  5. try {
  6. System.out.println(Thread.currentThread().getName()
  7. + ": 我还在工作!");
  8. Thread.sleep(1000);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. System.out.println(Thread.currentThread().getName() + ": 我结束了!");
  14. };
  15. Thread thread1 = new Thread(target, "李四");
  16. Thread thread2 = new Thread(target, "王五");
  17. System.out.println("先让李四开始工作");
  18. thread1.start();
  19. thread1.join();
  20. System.out.println("李四工作结束了,让王五开始工作");
  21. thread2.start();
  22. thread2.join();
  23. System.out.println("王五工作结束了");
  24. }
  25. }

运行结果如下:

我们可以看到王五线程会等李四线程结束了再开始工作,工作中某些场景可能会涉及到

0af61887a13942ac9a92375474e8c8a1.png

四、线程休眠

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
















方法 说明
public static void sleep(long millis) throws InterruptedException 休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos) throws InterruptedException 可以更高精度的休眠
  1. public class ThreadDemo {
  2. public static void main(String[] args) throws InterruptedException {
  3. System.out.println(System.currentTimeMillis());
  4. Thread.sleep(3 * 1000);//让此线程休眠3秒
  5. System.out.println(System.currentTimeMillis());
  6. }
  7. }

上面的代码演示是让上面的线程休眠3秒

五、获取当前线程引用












方法 说明
public static Thread currentThread(); 返回当前线程对象的引用
  1. public class ThreadDemo {
  2. public static void main(String[] args) {
  3. Thread thread = Thread.currentThread();//获取当前线程
  4. System.out.println(thread.getName());//获取当前线程的名字
  5. }
  6. }

本期内容就到这里,下期我会给大家分享更多内容

发表评论

表情:
评论列表 (有 0 条评论,255人围观)

还没有评论,来说两句吧...

相关阅读

    相关 mysqlbinlog用法详细说明

    mysqlbinlog用于处理二进制日志文件的实用工具详解mysqlbinlog 从二进制日志读取语句的工具。在二进制日志文件中包含的执行过的语句的日志可用来帮助从崩溃中恢

    相关 cron表达式用法详细说明

    cron表达式有至少6个(秒、分、时、日、月、星期)用空格隔开 7.年份(1970-2099) 其中每个元素可以是一个值(如6),一个连续区间(9-12),一个间隔时间(...