掘金 后端 ( ) • 2024-04-12 19:22

theme: healer-readable highlight: a11y-dark

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛

今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

在并发编程中,多线程是一种非常重要的技术手段,理论知识及线程创建我已经在上一章节讲过了,本章我们就不再赘述。我们都知道Java作为一种广泛应用的开发语言,提供了丰富的多线程编程支持。了解并掌握Java多线程的生命周期对于编写高效且可靠的多线程程序至关重要。所以在本文中,我将介绍Java多线程的生命周期,包括线程状态的转换和对应的方法调用,作为新一期的内容进行讲解,这也是为了帮助大家学习上能够循序渐进。

摘要

本文我的讲解思路是:首先通过简介介绍Java多线程的基本概念和特性,然后深入探讨线程的生命周期,包括线程的状态转换和相关的方法调用。此外,本文还将通过源代码解析、应用场景案例和优缺点分析等方式,帮助读者更好地理解和应用线程的生命周期。

简介

多线程是指在一个程序中同时进行多个线程的执行,每个线程具有独立的执行路径。在Java中,每个线程都有自己的生命周期,包括新建、就绪、运行、阻塞和终止等不同状态。线程状态的转换由Java虚拟机自动控制,但是开发人员可以通过方法调用来干预线程的状态。

源代码解析

在Java中,创建一个线程最常见的方式是继承Thread类或实现Runnable接口。以下是一个简单的示例代码:这里我们主要是以回顾+温习为主,毕竟上一章节已经学完的内容。

/**
 * @Author ms
 * @Date 2024-04-12 18:27
 */
public class MyRunnable implements Runnable {
    public void run() {
        // 线程执行的代码
        System.out.println("线程执行啦!");
    }
}


public class ThreadDemo {

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();

        System.out.println("主线程执行啦!");
    }
}

在上述代码中,MyThread类继承了Thread类,并重写了run方法。在ThreadDemo 类中,创建了一个MyThread实例,并调用start方法启动线程。

执行结果肯定如下所示:

在这里插入图片描述

还不够熟练地同学,这里也可以再多敲敲试一下。

应用场景案例

这里我们来谈谈,多线程可以应用于的各种场景,包括并发访问共享资源、提高程序性能、实现异步任务等。以下是几个常见的应用场景案例:

  1. 并发访问共享资源:多个线程同时访问同一个共享资源时,会出现竞态条件。使用多线程可以解决并发访问共享资源的问题,提高程序的执行效率。
  2. 提高程序性能:将耗时的任务拆分成多个子任务,用多线程同时执行,可以显著提高程序的运行速度。
  3. 实现异步任务:多线程可以在后台执行耗时的操作,让程序能够同时响应用户的输入和其他事件。

优缺点分析

多线程编程有很多优点,同时也存在一些缺点,毕竟任何事物都要辩证看待。以下是对多线程编程的优缺点分析:

优点:

  • 提高程序的执行效率,充分利用多核处理器的计算能力。
  • 改善用户体验,使程序能够同时响应用户的输入和其他事件。
  • 实现复杂的并发控制,提高程序的可扩展性和可维护性。

缺点:

  • 多线程编程更加复杂,需要考虑线程安全、竞态条件等问题。
  • 线程间的通信和同步开销较大,可能引入死锁和性能损失等问题。
  • 调试和问题排查更加困难,因为多线程程序的执行顺序不确定。

类代码方法介绍

在Java中,线程类常用的方法有以下几种:

  • start():启动线程,并调用线程的run方法。
  • sleep():使线程进入休眠状态,暂停执行一段时间。
  • join():等待线程执行完毕,再继续执行当前线程。
  • interrupt():中断线程的执行。

熟悉上述方法后,我们就要来实战演示了。

Java代码测试用例

以下是一个简单的Java多线程测试用例,用于计算从1到10的累加和,代码演示如下,仅供参考:

package com.example.javase.ms.threadDemo.day2;

/**
 * @Author ms
 * @Date 2024-04-12 19:09
 */
public class MyThread extends Thread {
    
    private static int sum = 0;

    public void run() {
        for (int i = 1; i <= 10; i++) {
            sum += i;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Sum is: " + sum);
    }
}

在上述代码中,MyThread类继承了Thread类,并重写了run方法。在主线程中,创建了两个MyThread实例,并分别启动两个线程。最后,使用join方法等待两个线程执行完毕,并打印累加和。

测试结果展示:

当程序运行时,它将创建两个线程,每个线程都会独立地计算从1到10的和。由于线程的执行是由操作系统的线程调度器控制的,所以两个线程完成计算的顺序是不确定的。但是,由于join()方法的使用,我们可以确保在主线程打印sum的值之前,两个线程都已经完成了它们的计算。结果展示如下:

在这里插入图片描述

其实上述代码,从执行结果上来看,不知道大家有没有发现问题!可以评论区交流下,虽然我下文也会给出对应的代码优化。

测试代码解析:

根据如上代码作出解析,以便于同学们更好的理解,分析如下:

如上测试用例展示了一个简单的多线程示例,其中创建了两个线程,每个线程都执行一个for循环来计算从1到10的整数和。这个示例使用了Thread类来创建和管理线程。

  1. MyThread类继承了Thread类,并重写了run()方法。在run()方法中,通过一个循环计算从1到10的整数和,并将结果累加到静态变量sum中。

  2. main方法中,创建了两个MyThread对象:thread1thread2,并且分别启动了这两个线程。

  3. thread1.join();thread2.join();这两行代码使得主线程等待thread1thread2完成执行。join()方法确保了在主线程继续执行之前,当前线程(在这个例子中是thread1thread2)已经终止。

  4. 最后,一旦两个线程都完成了它们的计算,主线程将打印出静态变量sum的值。

注意事项

  • 由于sum是一个静态变量,它在所有MyThread实例之间共享。这意味着两个线程都会访问和修改同一个sum变量,这可能导致线程安全问题,如竞态条件。这里就是我上述所提到的问题。
  • 在这个特定的例子中,由于每次只有一个线程在执行run()方法,所以不会出现竞态条件。但是,如果sum的更新是在多个线程中并发进行的,那么就需要使用同步机制来确保线程安全。

改进建议

那么如何避免潜在的线程安全问题呢?其实很简单,这里我们可以使用synchronized关键字来同步对sum的访问。优化代码如下:

/**
 * @Author ms
 * @Date 2024-04-12 19:14
 */
public class MyThread_update extends Thread {
    private static final Object lock = new Object();
    private static int sum = 0;

    public void run() {
        for (int i = 1; i <= 10; i++) {
            synchronized (lock) {
                sum += i;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread_update thread1 = new MyThread_update();
        MyThread_update thread2 = new MyThread_update();
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Sum is: " + sum);
    }
}

在这个改进的版本中,我们使用了一个对象lock作为同步锁。在每次更新sum变量时,我们都会获取这个锁,这样可以确保在同一时间只有一个线程能够执行更新操作。这有助于防止多个线程同时写入sum变量,从而避免竞态条件。

然后,我们再来执行测试一下,结果展示如下:

在这里插入图片描述

全文小结

本文介绍了Java多线程的生命周期,包括线程状态的转换和对应的方法调用。通过源代码解析、应用场景案例和优缺点分析等方式,帮助读者更好地理解和应用线程的生命周期。同时,通过Java代码测试用例演示了多线程的具体应用。

总结

了解和掌握Java多线程的生命周期对于编写高效且可靠的多线程程序至关重要。本文通过介绍线程的状态转换、方法调用和具体应用场景,为读者提供了丰富的知识和实践经验。通过合理地应用多线程技术,开发人员可以提高程序的执行效率,改善用户体验,并解决复杂的并发控制问题。

... ...

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

... ...

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。