java基本教程之join方法详解 java多线程教程

Java中的join()方法是多线程编程常用的一个方法,它的作用是让调用该方法的线程等待被调用线程执行完毕后再继续执行。本文将详细讲解join()方法的使用和注意事项。

什么是join()方法

在介绍join()方法之前,我们先回忆一下多线程的基础。在Java中,当创建一个线程对象并调用start()方法后,线程对象就会进入就绪状态,等待CPU分配时间片段并执行。当然,在实际应用中,很多时候我们需要协调不同线程的执行顺序。比如,在执行某些任务时,我们需要先让某个线程执行完毕后再执行其他线程。这时,就需要使用join()方法来实现。

join()方法是Thread类中的一个方法,它有一个可选参数,表示等待的时间,单位是毫秒。当某个线程调用了其他线程的join()方法时,它会被阻塞并等待其他线程执行完毕后继续执行。如果指定了超时时间,则等待的最大时间就是这个超时时间。

下面是一个示例代码,演示join()方法的基本用法:

public class JoinDemo {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("t1开始执行");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1执行完毕");
        });

        Thread t2 = new Thread(() -> {
            System.out.println("t2开始执行");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2执行完毕");
        });

        t1.start();
        t1.join(); // t1执行完毕后才会执行t2
        t2.start();
    }
}

在上面的代码中,我们首先创建了两个线程t1和t2,并分别用lambda表达式实现了它们的run()方法。我们希望在执行t2时先等待t1执行完毕,因此我们在t1调用完start()方法后接着调用了join()方法,指定t1执行完毕后才会执行t2。这样,t2就会一直等待t1执行完毕,直到t1执行完毕后才会开始执行。

运行上面的代码,输出结果如下:

t1开始执行
t1执行完毕
t2开始执行
t2执行完毕

可以看到,在调用了t1.join()方法后,主线程会一直等待t1执行完毕后再执行后续代码,因此先输出了t1执行完毕的语句。只有当t1执行完毕后,t2才会开始执行。

join()方法的注意事项

虽然join()方法是实现多线程协作的常用方法,但也有些需要注意的事项。

  1. join()方法抛出InterruptedException

当主线程或其他线程调用某个线程的join()方法时,如果该线程被中断,join()方法会抛出InterruptedException异常。因此,我们在调用join()方法时需要捕获该异常。

  1. join()方法会释放锁

当线程调用其他线程的join()方法时,它会持有被调用线程的锁,等待被调用线程执行完毕后,被调用线程释放锁后才会开始继续执行。这意味着,在调用join()方法时需要注意对共享变量的访问,以避免出现死锁等问题。

下面是一个示例代码,展示了join()方法可能导致的死锁问题:

public class JoinDeadlockDemo {

    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("t1持有lock1,等待lock2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("t1持有lock1和lock2");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("t2持有lock2,等待lock1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("t2持有lock1和lock2");
                }
            }
        });

        t1.start();
        t2.start();

        // 下面这两行代码会导致死锁
        t1.join(); 
        t2.join();
    }
}

在上面的代码中,我们创建了两个线程t1和t2,并通过synchronized关键字使它们持有lock1和lock2两个对象的锁。我们在t1和t2中都加入了一段等待时间和输出语句,在输出语句之前,先释放lock2的锁,再获取lock1的锁,确保执行t1和t2时都会先获取lock1对象的锁。

在主线程启动t1和t2后,接着调用了t1.join()和t2.join()方法,让主线程等待t1和t2执行完毕后再继续执行。由于t1持有lock1的锁,等待t2释放lock2的锁;而t2持有lock2的锁,等待t1释放lock1的锁。这就造成了t1和t2相互等待的死锁现象。

运行上面的代码,可以看到程序一直处于等待状态,没有任何输出。

为避免出现死锁等问题,在使用join()方法时,我们需要注意对共享变量的访问,并根据实际情况采取不同的策略。比如,可以通过设置适当的超时时间来避免死锁问题,或在持有锁的代码块中尽快释放锁等。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java基本教程之join方法详解 java多线程教程 - Python技术站

(0)
上一篇 2023年5月18日
下一篇 2023年5月18日

相关文章

  • Spring Boot启动过程全面解析(三)

    针对“SpringBoot启动过程全面解析(三)”这篇文章,我将进行以下详细讲解: 1. 文章简介 这篇文章主要讲解Spring Boot应用程序的启动过程。通过分析Spring Boot框架的源代码,介绍了Spring Boot启动时各个关键步骤的实现过程,帮助读者更好地理解Spring Boot框架的运作机制。 2. Spring Boot的静态资源加载…

    Java 2023年5月15日
    00
  • 细致解读希尔排序算法与相关的Java代码实现

    细致解读希尔排序算法与相关的Java代码实现 算法介绍 希尔排序(Shell Sort)是插入排序的一种高效的改进算法,也称作缩小增量排序,通过设定一个增量序列来先进行一定量的插入排序,然后逐步减小增量,最后增量为1时再进行一次插入排序,从而达到排序的效果。 希尔排序的过程如下: 设定一个增量序列(如:{1,3,7,15,…}),对于序列进行遍历; 对于…

    Java 2023年5月26日
    00
  • Spring Boot 2.x 把 Guava 干掉了选择本地缓存之王 Caffeine(推荐)

    下面我将详细讲解 Spring Boot 2.x 把 Guava 干掉了选择本地缓存之王 Caffeine(推荐)的攻略。 背景 在 Spring Boot 2.x 版本中,默认使用的是 Caffeine 作为本地缓存框架,而在之前的版本中,默认使用的是 Guava,这是因为,Caffeine 有更好的性能和更多的特性。 步骤 下面是使用 Caffeine …

    Java 2023年5月20日
    00
  • Java下Struts框架中的ActionForm类详解

    Java下Struts框架中的ActionForm类是用于从客户端浏览器向服务器端传递数据的载体。这个类作为中介,把客户端提交的数据(如表单数据)封装为一个JavaBean对象,然后该Bean就可以在服务器端通过Struts框架进行处理。 下面是ActionForm的使用步骤: 1. 定义ActionForm类 ActionForm类需要继承org.apac…

    Java 2023年5月20日
    00
  • Java面试题冲刺第二十九天–JVM3

    要讲解Java面试题冲刺第二十九天–JVM3的完整攻略,首先需要明确该篇文章的主要内容以及相关知识点和概念。 该篇文章主要是针对Java虚拟机的内存模型和内存管理机制进行讲解,包括JVM的内存结构、垃圾回收算法、性能监控工具等相关内容。以下是完整的攻略: JVM内存结构 JVM的内存结构主要包括以下几个部分: 方法区:存储已加载类的相关信息,如类信息、常量…

    Java 2023年5月19日
    00
  • eclipse maven 插件的安装和配置详解

    下面是“eclipse maven 插件的安装和配置详解”的完整攻略。 安装Eclipse Maven插件 打开Eclipse并切换到“Help”菜单,选择“Eclipse Marketplace”选项。 在“Eclipse Marketplace”搜索栏中输入“Maven”,然后点击“Go”按钮进行搜索。 在搜索结果中,找到“Maven Integrati…

    Java 2023年5月20日
    00
  • 如何使用JSP连接DB2数据库

    下面是使用JSP连接DB2数据库的完整攻略: 1. 配置DB2数据库和JDBC驱动 使用JSP连接DB2数据库需要先配置好数据库和JDBC驱动。这里以在Windows操作系统下为例子: 安装DB2数据库。安装过程不再赘述,安装完成后需要设置数据库登录账户和密码并启动服务。 下载DB2 JDBC驱动程序。可以在IBM的官网下载:https://www.ibm.…

    Java 2023年6月15日
    00
  • window.top[_CACHE]实现多个jsp页面共享一个js对象

    实现多个JSP页面共享一个JS对象,可以通过在不同的JSP页面中引入同一个JS文件来实现,但是如果需要在这些JSP页面通过JS互相访问/修改同一个对象,就需要使用window.top[_CACHE]机制。 下面是具体的实现步骤: 步骤1:定义一个全局的JS对象 在你的JS文件中,定义一个全局对象,例如: var mySharedObject = { coun…

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部