JVM 线程上下文类加载器
当前类加载器(Current ClassLoader)
每个类都会使用自己的类加载器(即加载自身的类加载器)来去加载其他类(指所依赖的类)
如果ClassX引用了ClassY,那么ClassX的类加载器就会去加载ClassY(前提是ClassY尚未被加载)
线程上下文加载器(Context Classloader)
线程上下文加载器是从JDK1.2开始引入,类Thread中的getContextLoader() 与setContextClassLoader(ClassLoader cl)
分别用来获取和设置上下文类加载器
如果没有通过setContextClassLoader(ClassLoader cl)进行设置的话,线程将继承其父线程的上下文加载器。
Java 应用运行时的初始线程的上下文加载器是系统类加载器。在线程中运行的代码可以通过该类加载器来加载类与资源。
线程上下文加载器的重要性:
SPI(Service Provider Interface)如JDBC
父ClassLoader可以使用当前线程Thread.currentThread().getContextClassLoader()所指定的classLoader加载的类
这就改变了父ClassLoader不能使用子ClassLoader或是或者其它没有父子关系的ClassLoader加载的类的情况,即改变了双亲委托模型
线程上下文类加载器就是当前线程的Current ClassLoader
如下面代码是连接使用JDBC连接Oralce
Connection是在rt.jar中的package java.sql;下,由启动类加载器加载。
具体Connection的实现是有Oracle厂商实现,会由AppClassLoader加载。
这样会导致的问题,启动类加载器加载的类不能访问AppClassLoader加载的类。
在双亲委托模型下,类的加载是由下至上的,即下层的类加载器会委托上层进行加载。但对于SPI来说,有些接口是Java核心库所提供的,
而Java核心库是由启动类加载器来加载的,而这些接口的实现却来自不同的Jar包(厂商提供JDBC的实现有Oracle,MySQL等), Java的启动类加载器不会加载其他
来源的jar包,这样传统的双亲委托模型就无法满足SPI的要求。而通过给当前线程设置上下文类加载器,就可以由设置的上下文类加载器
来实现对于接口实现类的加载。
1、创建MyTest24.java类
public class MyTest24 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getContextClassLoader()); //AppClassLoader
System.out.println(Thread.class.getClassLoader()); //null 启动类加载器
}
}
输出结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
null
2、创建MyTest25类
public class MyTest25 implements Runnable { private Thread thread; public MyTest25(){
thread = new Thread(this);
thread.start();
} @Override
public void run() {
ClassLoader classLoader = this.thread.getContextClassLoader();
this.thread.setContextClassLoader(classLoader);
System.out.println("Class:" +classLoader.getClass());
System.out.println("Parent:" + classLoader.getParent().getClass());
} public static void main(String[] args) {
new MyTest25(); }
}
运行结果:
Class:class sun.misc.Launcher$AppClassLoader
Parent:class sun.misc.Launcher$ExtClassLoader
说明:
如果没有通过setContextClassLoader(ClassLoader cl)进行设置的话,线程将继承其父线程的上下文加载器。
Java 应用运行时的初始线程的上下文加载器是系统类加载器。在线程中运行的代码可以通过该类加载器来加载类与资源。
默认设值在Launcher类中:
3、线程上下文类加载器的一般使用模式: 获取 - 使用 - 还原
如下面的伪代码:
//1、获取
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try{
//targetTccl 目标类加载器,想要使用的类加载器
Thread.currentThread().setContextClassLoader(targetTccl );
myMethod(); //2、使用
}finally{
// 3、还原
Thread.currentThread().setContextClassLoader(classLoader);
}
myMethod()里面调用了 Thread.currentThread().getContextClassLoader(),获取当前线程的上下文类加载器做某些事情(包括类的加载)。
如果一个类由加载器A加载,那么这个类的依赖类也是由相同的类加载器加载的(如果该依赖类之前没有被加载过的话)
ContextClassLoader的作用就是为了破坏Java的类加载委托机制。
当高层提供了统一的接口让底层去实现,同时又要在高层加载(或实例化) 底层的类时,就必须要通过类上下文类加载器来帮助高层的ClassLoader
找到并加载该类。
4、线程上下文类加载器的例子JDBC
JDBC的实现有不同的数据库尝试来实现,如Oracle和MySQL等。这里以MySQL为例
首先在build.gradle加载MySQL
compile(
"mysql:mysql-connector-java:5.1.34"
)
1)然后创建MyTest26类
public class MyTest26 { public static void main(String[] args) {
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
Iterator<Driver> iterator = loader.iterator();
while (iterator.hasNext()){
Driver driver = iterator.next();
System.out.println("driver:" + driver.getClass() + ",loader:" + driver.getClass().getClassLoader());
} System.out.println("当前线程上下文类加载器:" + Thread.currentThread().getContextClassLoader());
System.out.println("ServiceLoader类加载器:" + ServiceLoader.class.getClassLoader()); } }
打印结果:
driver:class com.mysql.jdbc.Driver,loader:sun.misc.Launcher$AppClassLoader@18b4aac2
driver:class com.mysql.fabric.jdbc.FabricMySQLDriver,loader:sun.misc.Launcher$AppClassLoader@18b4aac2
当前线程上下文类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
ServiceLoader类加载器:null
2)修改MyTest26类
public class MyTest26 { public static void main(String[] args) {
Thread.currentThread().setContextClassLoader(MyTest26.class.getClassLoader().getParent());
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
Iterator<Driver> iterator = loader.iterator();
while (iterator.hasNext()){
Driver driver = iterator.next();
System.out.println("driver:" + driver.getClass() + ",loader:" + driver.getClass().getClassLoader());
} System.out.println("当前线程上下文类加载器:" + Thread.currentThread().getContextClassLoader());
System.out.println("ServiceLoader类加载器:" + ServiceLoader.class.getClassLoader()); } }
增加了Thread.currentThread().setContextClassLoader(MyTest26.class.getClassLoader().getParent());
打印结果:
当前线程上下文类加载器:sun.misc.Launcher$ExtClassLoader@14ae5a5
ServiceLoader类加载器:null
这样就不会加载到了Driver
JVM 线程上下文类加载器的更多相关文章
- 【JVM学习笔记】线程上下文类加载器
有许多地方能够看到线程上下文类加载的设置,比如在sun.misc.Launcher类的构造方法中,能够看到如下代码 先写一个例子建立感性认识 public class Test { public st ...
- 线程上下文类加载器ContextClassLoader内存泄漏隐患
前提 今天(2020-01-18)在编写Netty相关代码的时候,从Netty源码中的ThreadDeathWatcher和GlobalEventExecutor追溯到两个和线程上下文类加载器Cont ...
- 7. 通过JDBC源码来分析线程上下文类加载器以及SPI的使用
目录 1. 什么是全盘负责委托机制 2. 为什么需要有线程上下文类加载器 2.1 使用JDBC的例子,分析为什么双亲委托机制不能实现要求 2.2 线程上下文类加载器的作用 3. 线程上下文类加载器的使 ...
- 深入理解Java类加载器(二):线程上下文类加载器
摘要: 博文<深入理解Java类加载器(一):Java类加载原理解析>提到的类加载器的双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器的实现方式.在Java ...
- 通过JDBC驱动加载深刻理解线程上下文类加载器机制
关于线程上下文类加载器已经在之前学得比较透了,作为一个收尾,这里用平常J2EE开发时JDBC连接Mysql数据库常见的一段代码通过分析它的底层进一步加深对线程上下文类加载器的理解,所以先来将连接应用代 ...
- 线程上下文类加载器(Context ClassLoader)
1.线程上下文类加载器是从jdk1.2开始引入的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader c1),分别用来 ...
- 【Java虚拟机11】线程上下文类加载器
前言 目前学习到的类加载的知识,都是基于[双亲委托机制]的.那么JDK难道就没有提供一种打破双亲委托机制的类加载机制吗? 答案是否定的. JDK为我们提供了一种打破双亲委托模型的机制:线程上下文类加载 ...
- 【深入理解JVM】:类加载器与双亲委派模型
类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字 ...
- JVM的艺术—类加载器篇(三)
JVM的艺术-类加载器篇(三) 引言 今天我们继续来深入的剖析类加载器的内容.上篇文章我们讲解了类加载器的双亲委托模型.全盘委托机制.以及类加载器双亲委托模型的优点.缺点等内容,没看过的小伙伴请加关注 ...
随机推荐
- 开发环境wamp3.06 + Zend studio 12 调试配置
<?php $fileName = "php大师.test.php"; //补充程序,显示文件名(不包括扩展名) $start = strrpos($fileName, &q ...
- 为群晖加把锁:使用ssh密钥保障数据安全
对每一个使用群晖nas的人而言,能保证群晖里保存的数据不被未经授权的人访问下载甚至破坏可能是最重要的事情.但数据只要上网,就免不了担心密码被破解,群晖被侵入.现在网络上,要破解密码可能是最简单不过的事 ...
- ab接口压力测试工具使用
ab的使用 模拟并发请求100次,总共请求10000次 命令模板: ab -c 100 -n 10000 待测试网站(建议完整路径) 内容解释: Server Software: nginx/1.10 ...
- CentOS 7.x 配置静态 IP 并启用
centos 7.x 配置静态 IP 并启用 0 问题由来 通过查询本机的IP,发现本机并没有有效IP: [pan@localhost ~]$ ip addr 所以,我们需要设置本机的静态IP,并启用 ...
- linux系统编程之进程(二)
今天继续学习进程相关的东东,上节提到了,当fork()之后,子进程复制了父进程当中的大部分数据,其中对于打开的文件,如果父进程打开了,子进程则不需要打开了,是共享的,所以首先先来研究下共享文件这一块的 ...
- 小白怎么用最短时间高效的学习Python?
之所以写这篇文章,在标题里已经表达得很清楚了.做技术的人都知道,时间就是金钱不是一句空话,同一个技术,你比别人早学会半年,那你就能比别人多拿半年的钱.所以有时候别人去培训我也不怎么拦着,为什么?因为培 ...
- HDU - 6125: Free from square (状压DP+分组背包)
problem:给定N,K.表示你有数1到N,让你最多选择K个数,问有多少种方案,使得选择的数的乘积无平方因子数.N,K<500: solution:显然可以状压DP做,但是500以内的素数还是 ...
- LG4783 【模板】矩阵求逆
P4783 [模板]矩阵求逆 题目描述 求一个$N\times N$的矩阵的逆矩阵.答案对$10^9+7$取模. 输入输出格式 输入格式: 第一行有一个整数$N$,代表矩阵的大小: 从第$2$行到第$ ...
- js比较日期大小 判断日期
使用js的方法来判断两个日期的先后关系,不能正常判断,因此手写了一个方法,如下: //判断开始日期是否大于结束日期,注意,该方法仅仅适用于“2010-01-01”这样的日期格式! function ...
- python内建函数和工厂函数的整理
内建函数参阅: https://www.cnblogs.com/pyyu/p/6702896.html 工厂函数: 本篇博文比较粗糙,后续会深入整理