• Java中Thread类的基本用法


    目录

    一、创建线程的方式

    1、继承Thread类

    2、实现Runnable接口

    3、匿名内部类中创建Thread子类对象

    4、匿名内部类中创建Runnable子类对象

    5、lambda表达式创建Runnabl子类对象

    二、Thread的常见构造方法

    三、启动一个线程

    四、获取当前线程 

    五、等待一个线程

    六、休眠当前线程

    七、中断当前线程

    线程中断的方法:


    一、创建线程的方式

    1、继承Thread类

    创建一个自定义的线程类并继承标准库中的Thread类:

    1. class MyThread extends Thread{
    2. @Override
    3. //重写父类的run方法
    4. public void run() {
    5. System.out.println("这是继承Thread类创建的线程");
    6. }
    7. }
    8. public class Thread1 {
    9. public static void main(String[] args) {
    10. //创建实例
    11. MyThread thread = new MyThread();
    12. //调用start方法后才会在系统上创建一个线程
    13. thread.start();
    14. }
    15. }

            其中,重写父类的run()方法是给MyThread类创建的线程安排任务,main()方法中创建一个MyThread实例后,调用父类的start()方法,新创建的线程就会执行子类的run()方法中的逻辑(此处发生了动态绑定)。

    代码运行结果:

    2、实现Runnable接口

    1. class MyThread2 implements Runnable{
    2. @Override
    3. public void run() {
    4. System.out.println("这是实现Runnable接口创建的线程");
    5. }
    6. }
    7. public class Thread2 {
    8. public static void main(String[] args) {
    9. Runnable runnable = new MyThread2();
    10. //需要将Runnable实例作为Thread构造方法的参数
    11. Thread thread = new Thread(runnable);
    12. thread.start();
    13. }
    14. }

    代码运行结果:

     

    3、匿名内部类中创建Thread子类对象

    1. public class Thread3 {
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(){
    4. @Override
    5. public void run() {
    6. System.out.println("匿名内部类创建Thread子类对象");;
    7. }
    8. };
    9. thread.start();
    10. }
    11. }

    代码运行结果:

    4、匿名内部类中创建Runnable子类对象

    1. public class Thread4 {
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(new Runnable() {
    4. @Override
    5. public void run() {
    6. System.out.println("匿名内部类创建Runnable子类对象");
    7. }
    8. });
    9. thread.start();
    10. }
    11. }

    代码运行结果:

    注意和匿名内部类创建Thread子类对象作区分

    5、lambda表达式创建Runnabl子类对象

    1. public class Thread5 {
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(()->{
    4. System.out.println("lambda表达式创建Runnable子类对象");
    5. });
    6. thread.start();
    7. }
    8. }

    代码运行结果:

    lambda表达式的写法比较简洁明了,推荐使用这种写法~

    二、Thread的常见构造方法

    方法说明
    Thread()创建线程对象
    Thread(Runnable target)使用Runnable对象创建线程对象(上述第二种创建线程的方式)
    Thread(String name)创建线程对象,并命名
    Thread(Runnable target,String name)使用Runnable对象创建线程对象并命名

            上表中第三个和第四个Thread的构造方法,可以给线程命名。我们先创建两个线程,一个是默认名,一个是自定义名:

    1. public static void main(String[] args) {
    2. Thread thread1 = new Thread(()->{
    3. while (true){
    4. System.out.println("hello thread");
    5. try {
    6. Thread.sleep(1100);
    7. } catch (InterruptedException e) {
    8. e.printStackTrace();
    9. }
    10. }
    11. },"这是一个自定义的线程");
    12. Thread thread2 = new Thread(()->{
    13. while (true){
    14. System.out.println("hello world");
    15. try {
    16. Thread.sleep(1100);
    17. } catch (InterruptedException e) {
    18. e.printStackTrace();
    19. }
    20. }
    21. });
    22. thread1.start();
    23. thread2.start();
    24. }

            线程的名字等信息可以在jconsole中看到:

            打开jconsole之前,需要确保线程在运行状态(所以上面两个线程都会死循环的打印内容),打开jconsole后,选择本地进程,找到正在运行的线程所在的类,点击连接:

    有了自定义线程名后,如果同时有多个线程在执行,我们在观察时就会方便很多。

    三、启动一个线程

            在创建一个线程实例后,并不会在操作系统上创建一个线程,只有当这个实例调用strat方法后,才会真正在操作系统上创建一个线程,然后该线程会去执行run方法中的代码,执行完run方法后,该线程就会被销毁。

    四、获取当前线程 

    通过类名调用currentThread()方法即可获取当前正在运行的线程的引用:

    1. public static void main(String[] args) {
    2. Thread thread = Thread.currentThread();
    3. System.out.println(thread.getName());//获取当前线程名字
    4. }

    代码运行结果:

    五、等待一个线程

    方法说明
    join()无时间限制地等待,直到线程结束
    join(long millis)最多等待millis毫秒
    join(long millis, int nanos)最多等待millis毫秒,nanos纳秒(更精确)

    示例:计算一个全局变量在一个线程中自增一千万次所需要的时间。

    1. static int count;
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(()->{
    4. for (int i = 0; i < 1000_0000; i++) {
    5. count++;
    6. }
    7. },"T");
    8. long beg = System.currentTimeMillis();//获取线程开始执行的时间
    9. thread.start();
    10. long end = System.currentTimeMillis();//获取线程结束执行的时间
    11. System.out.println("总共用时:"+(end - beg) + "ms");
    12. }

    如果直接这样写代码,就会发现运行结果为:

    原因:当前进程中有main线程和thread对象创建的T线程,两个线程在进程中是“并发”执行的,main线程并不会等到T线程执行完run方法中count的一千万次自增后再获取end的值,而是会不停地往下执行,即获取到beg的值后,会立刻获取end的值,因此end和beg的差值不足1ms。

    所以,我们需要使用join方法让main线程等到T线程执行完run方法之后再继续往后执行:

    1. static int count;
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(()->{
    4. for (int i = 0; i < 1000_0000; i++) {
    5. count++;
    6. }
    7. },"T");
    8. long beg = System.currentTimeMillis();//获取线程开始执行的时间
    9. thread.start();
    10. try {
    11. thread.join();
    12. } catch (InterruptedException e) {
    13. e.printStackTrace();
    14. }
    15. long end = System.currentTimeMillis();//获取线程结束执行的时间
    16. System.out.println("总共用时:"+(end - beg) + "ms");
    17. }

    调用join方法可能会抛出异常,使用 try-catchthrows 处理一下即可。

    再次运行代码的结果:

    六、休眠当前线程

    方法说明
    sleep(long millis)休眠当前线程millis毫秒
    sleep(long millis,int nanos)休眠当前线程misslis毫秒,nanos纳秒

            sleep方法也是一个静态方法,需要通过类名进行调用。作用是让当前线程休眠一段时间后再继续往后执行,调用sleep方法也需要处理InterruptedException这个异常。

    示例: 让main线程休眠5秒再往后执行。

    1. public static void main(String[] args) throws InterruptedException {
    2. long beg = System.currentTimeMillis();
    3. Thread.sleep(5000);
    4. long end = System.currentTimeMillis();
    5. System.out.println("main线程休眠时间:"+(end-beg)+"ms");
    6. }

    你以为会打印5000ms?实际上打印的是:

    调用sleep方法后线程会进入阻塞状态,休眠完5000ms后线程才会恢复到就绪状态,状态之间的切换也会消耗时间,因此线程的实际休眠时间会大于参数设置的时间。

    七、中断当前线程

            根据上文可以知道,线程在执行完run()方法后才会结束,那么可不可以让线程提前结束呢?

            线程中断就是让线程提前结束的方式(本质上是让run方法提前结束,并非是在run方法执行过程中强制结束进程),但是能否提前结束也取决于run方法是如何实现的。

    线程中断的方法:

    (1) 自定义一个标志位,作为线程是否结束的标记

    1. public class Test {
    2. static boolean isQuit;//自定义的标志位
    3. public static void main(String[] args) throws InterruptedException {
    4. Thread thread = new Thread(()->{
    5. while (!isQuit){
    6. System.out.println("hello thread");
    7. try {
    8. Thread.sleep(1000);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. }
    13. System.out.println("T线程结束");
    14. },"T");
    15. thread.start();
    16. Thread.sleep(3000);//main线程休眠3秒
    17. isQuit = true;//标志位设置为true
    18. System.out.println("设置让T线程提前结束");
    19. }
    20. }

    代码运行结果:

            本来T线程会在run方法中死循环的打印"hello thread",但是我们通过设置标志位,让T线程提前结束。

    (2)使用标准库中的标志位

    1. public static void main(String[] args) throws InterruptedException {
    2. Thread thread = new Thread(()->{
    3. while (!Thread.currentThread().isInterrupted()){
    4. System.out.println("hello thread");
    5. try {
    6. Thread.sleep(1000);
    7. } catch (InterruptedException e) {
    8. e.printStackTrace();
    9. }
    10. }
    11. System.out.println("T线程结束");
    12. },"T");
    13. thread.start();
    14. Thread.sleep(3000);
    15. thread.interrupt();//设置标志位为true
    16. System.out.println("设置标志位让T线程提前结束");
    17. }

            上述代码中的isInterrupted()方法是判断当前线程是否被中断,线程在正常运行时,该方法的返回值是false,即未被中断。而interrupt()方法则是将当前线程设置为中断状态,即将isInterrupted()方法的返回值设置为true

            代码执行结果:

            从上图中可以发现,将标志位设置为True后,T线程在抛出一个异常信息后继续循环打印“hello thread”,线程并没有结束。

            因为thread在调用interrupt()方法时,T线程存在两种状态:

    (1)运行状态

            此时调用interrupt()方法,可以顺利地中断进程。

    (2)阻塞状态

            T线程在调用sleep()方法后就会进入阻塞状态,此时调用interrupt()方法,不会设置标志位为True,而是会把T线程从阻塞状态提前唤醒,抛出InterruptedException异常。

            所以我们要想中断进程,只需要在抛出异常时加一个break即可:

            在Java中,中断线程并不一定是强制执行的,而是由线程自身进行判定处理(取决于代码是如何实现的),一般有三种处理方式:

    (1)立即结束:直接break

    (2)不予理会:不作处理,继续执行run方法的代码

    (3)稍后结束:休眠一段时间再break

  • 相关阅读:
    中秋节静态HTML网页作业作品 大学生中秋网页设计制作成品 简单DIV CSS布局网站
    [sciter]:sciter如何使用i18实现桌面应用多语言切换及其利弊
    ASEMI肖特基二极管和超快恢复二极管在开关电源中的对比
    umi4+vue3开发模板摸索
    微服务TraceId设计(SpringCloud OpenFeign)
    使用binlog备份恢复myqsl数据
    主流的网络计算模式有哪些
    【Airtest】实现UI自动化测试(一)
    2000-2022年“宽带Z国“试点城市名单匹配数据
    RedisTemplate用法
  • 原文地址:https://blog.csdn.net/m0_67683346/article/details/126426440