真正理解线程上下文类加载器(多案例分析)

前置知识: java类加载器不完整分析

#前言
此前我对线程上下文类加载器(ThreadContextClassLoader,下文使用TCCL表示)的理解仅仅局限于下面这段话:

Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。

这些 SPI 的接口由 Java 核心库来提供,而这些 SPI 的实现代码则是作为 Java 应用所依赖的 jar 包被包含进类路径(CLASSPATH)里。SPI接口中的代码经常需要加载具体的实现类。那么问题来了,SPI的接口是Java核心库的一部分,是由**启动类加载器(Bootstrap Classloader)来加载的;SPI的实现类是由系统类加载器(System ClassLoader)**来加载的。引导类加载器是无法找到 SPI 的实现类的,因为依照双亲委派模型,BootstrapClassloader无法委派AppClassLoader来加载类。

而线程上下文类加载器破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。

一直困恼我的问题就是,它是如何打破了双亲委派模型?又是如何逆向使用类加载器了?直到今天看了jdbc的驱动加载过程才茅塞顿开,其实并不复杂,只是一直没去看代码导致理解不够到位。

JDBC案例分析

我们先来看平时是如何使用mysql获取数据库连接的:

// 加载Class到AppClassLoader(系统类加载器),然后注册驱动类
// Class.forName("com.mysql.jdbc.Driver").newInstance(); 
String url = "jdbc:mysql://localhost:3306/testdb";    
// 通过java库获取数据库连接
Connection conn = java.sql.DriverManager.getConnection(url, "name", "password"); 

以上就是mysql注册驱动及获取connection的过程,各位可以发现经常写的Class.forName被注释掉了,但依然可以正常运行,这是为什么呢?这是因为从Java1.6开始自带的jdbc4.0版本已支持SPI服务加载机制,只要mysql的jar包在类路径中,就可以注册mysql驱动。

那到底是在哪一步自动注册了mysql driver的呢?重点就在DriverManager.getConnection()中。我们都是知道调用类的静态方法会初始化该类,进而执行其静态代码块,DriverManager的静态代码块就是:

static {
   
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

初始化方法loadInitialDrivers()的代码如下:

private static void loadInitialDrivers() {
   
    String drivers;
    try {
   
		// 先读取系统属性
		drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
   
            public String run() {
   
                return System.getProperty("jdbc.drivers");
            }
        });
    } catch (Exception ex) {
   
        drivers = null;
    }
    // 通过SPI加载驱动类
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
   
        public Void run() {
   
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
   
                while(driversIterator.hasNext()) {
   
                    driversIterator.next();
                }
            } catch(Throwable t
  • 119
    点赞
  • 365
    收藏
    觉得还不错? 一键收藏
  • 115
    评论
线程中的上下文切换是指在多个线程之间切换执行的过程。当一个线程正在执行时,如果有其他线程需要执行,操作系统会暂停当前线程的执行,并保存其当前的上下文(包括程序计数器、寄存器值、栈指针等信息),然后切换到下一个线程上下文,使其开始执行。这个过程就是上下文切换。 上下文切换是操作系统进行调度的基本机制之一,它可以实现多个线程并发执行,提高系统的资源利用率。然而,上下文切换也会带来一定的开销,因为保存和恢复上下文需要消耗额外的时间和资源。 上下文切换的开销主要包括以下几个方面: 1. 保存和恢复寄存器状态:因为每个线程都有自己的寄存器状态,所以在上下文切换时需要保存当前线程的寄存器状态,并恢复下一个线程的寄存器状态。 2. 切换内核栈:每个线程都有自己的内核栈,用于保存临时变量和函数调用信息。在上下文切换时,需要切换内核栈。 3. 更新页表:当线程切换时,可能会涉及到虚拟内存的映射关系变化,需要更新页表来确保正确的地址访问。 4. 刷新硬件状态:上下文切换还可能需要刷新一些硬件状态,如缓存和TLB等。 上下文切换的频率过高会导致系统性能下降,因此在编写多线程程序时,需要合理设计线程的数量和调度策略,以尽量减少上下文切换的次数。
评论 115
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值