当前位置: 首页 > news >正文

多线程的创建方式以及及Thread类详解

目录

一.线程的创建方法:(重点)

        一:继承Thread类

写法一:正常写法

写法二:匿名内部类 

二.实现Runnable接口 

写法一:正常写法

 写法二:匿名内部类 

三. 实现 Callable 接口 

​编辑

四.通过线程池创建线程

二.Thread类及常⻅⽅法

1.Thread的常⻅构造⽅法

2.Thread的⼏个常⻅属性

3.Thread常见方法(重点)

run()方法 和start()方法(区别)

join()方法

 中断线程

sleep()方法 


一.线程的创建方法:(重点)

        一:继承Thread类

写法一:正常写法

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务

  2. 创建Thread子类的实例,即创建了线程对象

  3. 调用线程对象的start()方法来启动该线程

public class MyThread  extends  Thread{@Overridepublic void run() {while (true) {System.out.println("这里是子线程");}}public static void main(String[] args) {MyThread thread1 = new MyThread();thread1.start();while (true) {System.out.println("这里是主线程");}}
}

 

写法二:匿名内部类 

    public static void main(String[] args) {Thread thread = new Thread() {@Overridepublic void run() {while (true) {System.out.println("这里是thread线程");}}};thread.start();while (true) {System.out.println("这里是main线程");}}


二.实现Runnable接口 

写法一:正常写法

  1. 创建一个实现了Runnable接口的类,并实现接口中的run()方法,定义线程的执行逻辑。
  2. 在主线程中创建Runnable实例,并将其作为参数传递给Thread类的构造方法。
  3. 调用Thread对象的start()方法启动线程。
public class MyRunnable implements Runnable{@Overridepublic void run() {while (true) {System.out.println("thread!");}}public static void main(String[] args) {MyRunnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start();while (true) {System.out.println("main!");}}
}

 

 写法二:匿名内部类 

    public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {System.out.println("thread!");}}});thread.start();while (true) {System.out.println("main!");}}

三. 实现 Callable 接口 

前两种方式都存在一个问题:重写的run方法均不能直接返回结果,不适合需要返回线程执行结果的场景。而通过实现Callable接口则可以做到这一点。

  1. 得到任务对象定义类实现Callable接口,重写call方法,封装要做的事情,用FutureTask把Callable对象封装成线程任务对象
  2. 把线程任务对象交给Thread处理
  3. 调用Thread的start方法启动线程,执行任务
  4. 线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果
public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {for (int i = 0; i < 3; i++) {System.out.println("thread:" + i);}return "call!";}public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable myCallable = new MyCallable();FutureTask<String> futureTask = new FutureTask<>(myCallable);Thread thread = new Thread(futureTask);thread.start();for (int i = 0; i < 3; i++) {System.out.println("main:"+i);}System.out.println(futureTask.get());}
}


四.通过线程池创建线程

后序再详讲


二.Thread类及常⻅⽅法

1.Thread的常⻅构造⽅法

  • Thread t1 = new Thread();
  • Thread t2 = new Thread(new MyRunnable());
  • Thread t3 = new Thread("这是我的名字");
  • Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2.Thread的⼏个常⻅属性

  •  ID是线程的唯⼀标识,不同线程不会重复
  • 名称是各种调试⼯具⽤到
  •  状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明
  •  优先级⾼的线程理论上来说更容易被调度到
  •  关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
  • 是否存活,即简单的理解,为run⽅法是否运⾏结束了
  • 线程的中断问题,下⾯我们进⼀步说明 

3.Thread常见方法(重点)

run()方法 和start()方法(区别)

  • start()方法是Thread类中的一个方法,用于启动一个新的线程。当调用start()方法时,系统会创建一个新的线程,并在新的线程中执行run()方法的内容。start()方法会在新的线程中执行一些准备工作,然后调用run()方法。
  • run()方法是实现了Runnable接口的类中的一个方法。在启动一个线程后,系统会自动调用该线程对象的run()方法。run()方法中包含了线程的主体代码,即线程要执行的任务。

2.start()方法

public class MyThread extends Thread{@Overridepublic void run() {while (true) {System.out.println("这里是thread");}}public static void main(String[] args) {MyThread thread = new MyThread() ;thread.start();while (true) {System.out.println("这里是main线程");}}
}

此时为俩个线程并发处理

1.run()方法

 public class MyThread extends Thread{@Overridepublic void run() {while (true) {System.out.println("这里是thread");}}public static void main(String[] args) {MyThread thread = new MyThread() ;thread.run();while (true) {System.out.println("这里是main线程");}}
}

此时只实现了run方法 并没有并发处理 

总结:

  • start() 方法 用于启动一个新线程,它会触发线程的创建和调度,最终由操作系统负责执行 run() 方法。
  • run() 方法 是线程的实际执行任务,是线程执行的代码部分。如果直接调用 run() 方法,它会在当前线程中执行,并不会启动新的线程。

join()方法

在多个线程中,他们的执行顺序属于系统调动的,是无序的(抢占式执行)

而我们希望一个稳定的顺序,如何执行?

join() 方法的使用场景:

场景 1:确保多个线程按顺序执行

有时,你可能希望在主线程中启动多个子线程,并确保在继续执行后续任务之前,所有子线程已经完成。例如,在处理多个任务并且它们之间有依赖关系时,必须等待所有线程完成。

场景 2:等待线程完成后汇总结果

在多线程并发计算时,主线程可能需要等待多个线程完成任务,才能进行汇总或处理结果。join() 方法可以帮助你等待所有线程执行完成后再进行下一步操作。

场景 3:避免主线程提前退出

如果没有使用 join(),主线程可能会在子线程完成之前就退出,导致子线程未执行完成。使用 join() 可以保证主线程在所有子线程完成之前不会退出。

 

案例:

class MyThread extends Thread {private String name;public MyThread(String name) {this.name = name;}public void run() {System.out.println(name + " 正在执行...");try {Thread.sleep(1000); // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + " 执行完毕!");}
}public class Main {public static void main(String[] args) throws InterruptedException {MyThread thread1 = new MyThread("线程1");MyThread thread2 = new MyThread("线程2");MyThread thread3 = new MyThread("线程3");thread1.start();thread2.start();thread3.start();// 主线程等待所有子线程执行完毕thread1.join();thread2.join();thread3.join();System.out.println("所有线程都已执行完毕,主线程继续执行...");}
}

  • 在这个例子中,主线程通过调用 thread1.join()thread2.join() 和 thread3.join() 来等待所有子线程执行完毕。
  • 主线程会阻塞,直到 thread1thread2 和 thread3 执行完成。
  • 当所有线程执行完毕后,主线程输出 "所有线程都已执行完毕,主线程继续执行..."

总结:

  1. 确保线程顺序:有时我们希望线程按照特定的顺序执行。使用 join() 可以让主线程等待子线程执行完毕,这样就能保证主线程在子线程之后才继续执行。

  2. 避免主线程提前退出:在多线程应用中,如果主线程没有等待子线程完成就退出,可能导致子线程未完成时程序就结束了,特别是在没有使用 join() 时,主线程可能比子线程先结束。

  3. 汇总线程结果:如果多个线程在并行执行,主线程可能需要等待它们执行完毕后再进行结果汇总。join() 方法能够确保线程完成后,主线程能够获取到所有线程的执行结果。

  4. 避免线程竞态条件:通过确保线程按顺序执行,join() 可以避免一些可能的竞态条件和线程同步问题。


 中断线程

在 Java 多线程编程中,中断线程是一种用于请求线程停止执行的机制。线程的中断并不意味着直接停止线程的执行,而是给线程发送一个中断信号,让线程有机会在合适的地方处理这个信号并终止。线程中断通常用于协调多个线程的生命周期,特别是在需要停止一个线程的执行时,或者是某个线程的操作不再需要时

线程中断的使用场景:

  1. 停止正在执行的线程: 当线程正在进行耗时的任务时,如果你需要在某个时刻停止它,可以通过中断来实现。中断信号告诉线程它可以停止工作或清理资源并退出。

    应用场景:比如有一个后台线程在执行某些长时间的计算任务或下载操作,如果用户取消了操作,你可以通过中断线程来停止这些任务。

  2. 响应外部中断请求: 比如在一个多线程的客户端应用中,如果用户要求退出程序或者停止某些操作,可以通过发送中断信号来优雅地停止正在运行的线程。

    应用场景:比如在一个多线程的 HTTP 请求处理系统中,如果服务器需要停止处理请求,可以通过中断正在处理请求的线程。

  3. 在阻塞操作中响应中断: 当线程执行如 sleep()join()wait() 等阻塞操作时,可以中断线程来提前终止阻塞操作,从而使线程能够继续执行其他任务。

    应用场景:在多线程的网络编程中,可能需要等待其他线程的响应,如果等待超时,则可以通过中断等待线程,防止程序长时间处于等待状态。

  4. 取消正在执行的任务: 在一些任务执行框架中,可以通过中断来取消正在执行的任务。当任务执行时间过长,或者任务本身不再需要时,可以通过中断来提示线程终止。

    应用场景:比如在处理大量数据时,当用户主动取消任务时,可以中断相关的线程。

 案例1:使用 Thread.interrupt() 中断线程

public class InterruptExample {public static void main(String[] args) {// 创建线程Thread thread = new Thread(() -> {try {System.out.println("线程开始执行");// 模拟一个长时间的任务for (int i = 0; i < 10; i++) {if (Thread.currentThread().isInterrupted()) {System.out.println("线程被中断,退出循环");return;}Thread.sleep(1000);  // 模拟工作System.out.println("线程正在工作,执行第 " + (i + 1) + " 次");}} catch (InterruptedException e) {System.out.println("线程在阻塞时被中断:" + e.getMessage());}});thread.start();try {// 等待一会儿,然后中断线程Thread.sleep(3000);thread.interrupt();  // 请求中断线程} catch (InterruptedException e) {e.printStackTrace();}}
}

示例 2:线程阻塞时的中断 

public class InterruptInSleepExample {public static void main(String[] args) {Thread thread = new Thread(() -> {try {System.out.println("线程开始执行,准备休眠");Thread.sleep(5000);  // 模拟长时间的阻塞操作System.out.println("线程正常执行完毕");} catch (InterruptedException e) {System.out.println("线程被中断,异常信息:" + e.getMessage());}});thread.start();try {// 等待 2 秒钟然后中断线程Thread.sleep(2000);thread.interrupt();  // 请求中断线程} catch (InterruptedException e) {e.printStackTrace();}}
}

总结

  • 中断线程是通过 Thread.interrupt() 方法发出请求,线程可以通过检查中断状态来响应这个请求。
  • 常见的使用场景包括:停止长时间运行的任务、在阻塞操作中响应外部请求、取消正在进行的操作等。
  • 线程中断是一种“协作式的停止”,即线程需要在合适的地方主动检查中断状态并退出。

sleep()方法 

在Java中,sleep() 方法是 Thread 类中的一个静态方法,用于暂停当前执行的线程指定的时间

案例:定时打印信息

public class TimerTaskExample {public static void main(String[] args) {Long start = System.currentTimeMillis();for (int i = 1; i <= 5; i++) {System.out.println("第 " + i + " 次执行");try {// 每次执行后休眠1秒Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}Long end = System.currentTimeMillis();System.out.println("总共耗时:"+(end-start));}
}


结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力! 


http://www.mrgr.cn/news/70071.html

相关文章:

  • 深度学习——AE、VAE
  • 关于在VS中使用Qt不同版本报错的问题
  • Android关机流程知多少?
  • DNS Resolver解析服务器出口IP查询
  • 【数据结构】图的应用的时间复杂度
  • vue-h5:在h5中实现相机拍照加上身份证人相框和国徽框
  • 2024下半年系统架构师考试【回忆版】
  • 大数据程序猿不可不看的资料大全
  • 【模型】EfficientvitSAM
  • PGMP-串串01概述
  • multi_agents
  • 10个文献翻译工具推荐,实现专业翻译的好帮手。
  • 多处理器一致协议(MSI)协议详细介绍
  • #渗透测试#SRC漏洞挖掘#深入挖掘CSRF漏洞01
  • Linux学习笔记之软件包管理RPM与YUM
  • 渗透测试-网络基础(1)
  • 时序预测 | 改进图卷积+informer时间序列预测,pytorch架构
  • 《Docker镜像与容器技术基础操作及应用研究》
  • shodan5(泷羽sec)
  • core-js 解决浏览器兼容性问题的工具之一
  • css3D变换用法
  • langgraph_plan_and_execute
  • 青龙面板脚本--阿里云盘任务脚本
  • 【网络安全】Java线程安全及非线程安全
  • 最简单解决NET程序员在centos系统安装c#网站
  • 网络安全之SQL初步注入