Java中线程上下文类加载器超详细讲解使用

Java中线程上下文类加载器超详细讲解使用

前言

在Java多线程中,经常会出现跨类加载器的情况,例如Web容器中的应用程序的类加载器和Java线程在使用的类加载器可以是不同的实例。而在Java中,不同的类加载器对于同一个类的加载得到的Class对象实例是不同的,这样就会导致在不同的类加载器中创建的对象实例无法相互转换,从而引发一系列问题。为此,Java中引入了线程上下文类加载器的概念。本文将会对这个概念进行详细讲解,并且提供两条实例。

线程上下文类加载器是什么?

线程上下文类加载器是Java提供的一种机制,在多线程的场景中可以让当前线程加载指定的类,从而解决了跨类加载器的问题。每个线程都可以通过设置上下文类加载器,让该线程在加载类时优先使用这个类加载器。当该线程找不到某个类时,将会委托其上下文类加载器去加载这个类。

线程上下文类加载器的使用方法

Java提供了Thread类中的getContextClassLoader()和setContextClassLoader(ClassLoader cl)方法,这两个方法可以操作线程的上下文类加载器。下面分别对它们进行说明。

getContextClassLoader()方法

getContextClassLoader()方法用来获取线程的上下文类加载器,方法签名如下:

public ClassLoader getContextClassLoader()

调用该方法可以得到该线程当前正在使用的上下文类加载器,如果未设置该线程的上下文类加载器,则返回父线程的上下文类加载器(默认情况下,所有线程的上下文类加载器都是其父线程的上下文类加载器)。

setContextClassLoader(ClassLoader cl)方法

setContextClassLoader(ClassLoader cl)方法用于设置线程的上下文类加载器,方法签名如下:

public void setContextClassLoader(ClassLoader cl)

该方法接受一个类加载器作为参数,用于设置该线程的上下文类加载器为传入的类加载器。如果不设置该线程的上下文类加载器,那么设置即使不设置,默认情况下,该线程的上下文类加载器会继承父线程的上下文类加载器。

线程上下文类加载器的使用示例

Java中线程上下文类加载器的使用场景很多,这里我们提供两个示例来说明。

示例1

在Java中很多第三方框架都使用了SPI (Service Provider Interface)机制,在SPI机制中,服务提供者会提供一个标准的接口,并会将该接口实现类的全类名记录在一个配置文件中,类似于 Java中的META-INF/service目录中的文件。这样当应用程序运行时,使用该接口的程序可以通过在配置文件中查找实现类的全类名,并通过反射机制创建该实现类的实例。

在SPI机制中,如果服务提供商和服务使用者不在同一个类加载器中,那么在进行实例初始化时就会报类转换异常。此时,可以通过设置线程上下文类加载器来解决该问题,代码示例如下:

public class MyServiceLoader<T> {

    private static final String PREFIX = "META-INF/services/";

    private Class<T> service;

    public MyServiceLoader(Class<T> service) {
        this.service = service;
    }

    public T load() throws Exception {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        String file = PREFIX + service.getName();
        Enumeration<URL> urls = contextClassLoader.getResources(file);
        while (urls != null && urls.hasMoreElements()) {
            URL url = urls.nextElement();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    Class<?> clazz = contextClassLoader.loadClass(line);
                    if (clazz != null && service.isAssignableFrom(clazz)) {
                        return (T) clazz.newInstance();
                    }
                }
            }
        }
        return null;
    }
}

示例2

当应用程序使用了一些比较底层的Java库时,就会可能出现跨类加载器的情况。例如,有一个app.jar包中的类需要使用Java标准库中的类。但是如果使用了不同的类加载器,就可能会造成类转换异常,因为这些类都不在同一个类加载器中。此时,可以通过在Thread.currentThread().setContextClassLoader(ClassLoader)中设置上下文类加载器为根类加载器来解决该问题,代码示例如下:

public class App {

    public static void main(String[] args) throws Exception {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
        //使用根类加载器加载必要的类
        Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass("java.lang.String");
        System.out.println(clazz.getClassLoader());
        //...
        //使用完还原线程上下文类加载器
        Thread.currentThread().setContextClassLoader(contextClassLoader);
    }

}

结论

线程上下文类加载器是Java应用程序中处理跨类加载器问题的有力工具,理解和掌握这个机制对于进行Java开发工作至关重要。当应用程序中存在跨类加载器的情况时,可以通过设置线程上下文类加载器来解决这些问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中线程上下文类加载器超详细讲解使用 - Python技术站

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

相关文章

  • Java之使用POI教你玩转Excel导入与导出

    Java之使用POI教你玩转Excel导入与导出 什么是POI POI是一个Java开发的用于操作Microsoft Office格式文件的开源框架。POI可以读写文档、演示文稿、Excel文件等,并且支持多种文件格式。下面我们主要讲解POI在Java中如何操作Excel文件的导入与导出。 Excel文件的导入 准备工作 首先,我们需要在pom.xml文件中…

    Java 2023年5月19日
    00
  • docker常用命令

    一、Docker基本概念 1.镜像(Image) Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。 docker的镜像是由 镜像名+版本 组成的。如果没有指定镜像名没有指定版本,默认是…

    Java 2023年4月23日
    00
  • 如何基于SpringMVC实现断点续传(HTTP)

    基于SpringMVC实现断点续传(HTTP) 断点续传是指在文件传输过程中,如果传输中断,可以从中断处继续传输,而不需要重新传输整个文件。在本文中,我们将详细介绍如何基于SpringMVC实现断点续传(HTTP)。 步骤一:添加依赖 在使用SpringMVC框架之前,我们需要在项目中添加SpringMVC依赖。我们可以在pom.xml文件中添加以下依赖: …

    Java 2023年5月17日
    00
  • Spring Boot超详细讲解请求处理流程机制

    Spring Boot超详细讲解请求处理流程机制 Spring Boot是一个非常流行的Java Web框架,它提供了许多方便的功能,如自动配置、快速开发和易于部署。在开发过程中,我们需要了解Spring Boot的请求处理流程机制,以便更好地理解应用程序的工作原理。本文将详细介绍Spring Boot的请求处理流程机制,并提供两个示例。 请求处理流程机制 …

    Java 2023年5月15日
    00
  • UML类图

    UML类图介绍 概念 UML中的类图(Class Diagram)用于表示类、接口、实例等之间相互的静态关系。虽然名字叫作类图,但是图中并不仅仅只有类。 类结构 继承 该图展示了Parentclass和Childclass两个类之间的关系,其中的空心箭头表明了两者之间的层次关系。箭头由子类指向父类,换言之,这是表示继承(extends)的箭头。ParentC…

    Java 2023年4月22日
    00
  • JavaScript中将字符串转换为数字的七种方法总结

    下面是详细讲解“JavaScript中将字符串转换为数字的七种方法总结”的攻略。 攻略 1. 使用parseFloat()函数将字符串转换为浮点数 可以使用JavaScript内置的parseFloat()函数将字符串转换为浮点数。该函数会尝试将给定的字符串解析为一个浮点数,并返回一个浮点数结果。 示例: var str = "3.14"…

    Java 2023年5月27日
    00
  • java lambda表达式用法总结

    Java Lambda表达式用法总结 什么是Lambda表达式 Lambda表达式是Java 8中引入的一种新特性,可以用于创建一个匿名函数,从而大大简化了代码的编写。它可以看做是一种语法糖,用于简化某些类型的方法的声明。 在Java中,Lambda表达式由两部分组成: 参数列表:可以包含0个或多个参数,多个参数用逗号隔开。 代码块:可以是任意的Java代码…

    Java 2023年5月26日
    00
  • servlet之web路径问题_动力节点Java学院整理

    当开发Servlet时,我们通常会遇到一些Web路径相关的问题,这篇攻略将会详细讲解这些问题,并提供相应的解决方法。 1. Servlet中的Web路径问题 在Servlet中,一般涉及到两种类型的Web路径:绝对路径和相对路径。在处理这些路径时,我们需要了解以下内容: Web应用的根路径 Servlet映射路径 Servlet所在的包路径 1.1 Web应…

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