package com.company;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
public class Main {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
new Thread(new Runnable() {
@Override
public void run() {
try {
URL[] urls = new URL[1];
urls[0] = new File("/Users/wf/IdeaProjects/thread/dom4j-2.1.3.jar").toURI().toURL();
URLClassLoader urlClassLoader = new URLClassLoader(urls);
mainThread.setContextClassLoader(urlClassLoader);
System.out.println("在" + Thread.currentThread().getName() + "设置了主线程的自定义 classLoader " + urlClassLoader);
} catch (Exception e) {
e.printStackTrace();
}
}
}, "线程 1").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "上下文 设置前的 classloader" + Thread.currentThread().getContextClassLoader());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, "线程 2").start();
}
}
首先我不知道标题应该怎么起更好一些,
是的,我看了部分文章,代码运行的结果,跟我想象中的一样。(子线程会一直使用父线程的 classLoader 并且还是实时更新的)
1
zxlzy 2021-12-09 11:45:33 +08:00
然而并不能啊。在设置 classLoader 前加个 Thead.sleep() 就知道不能了。你的结论就是错的。本质上是线程 1 先运行线程 2 才运行的。
```java public class Main { public static void main(String[] args) { Thread mainThread = Thread.currentThread(); new Thread(new Runnable() { @Override public void run() { try { ClassLoader cl = new ClassLoader() { @Override public String getName() { return "MyCloassLoader"; } }; mainThread.setContextClassLoader(cl); System.out.println("在" + Thread.currentThread().getName() + "设置了主线程的自定义 classLoader " + cl); } catch (Exception e) { e.printStackTrace(); } } }, "线程 1").start(); new Thread(new Runnable() { @Override public void run() { try { for (int i = 0; i < 100; i++) { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "上下文 设置前的 classloader" + Thread.currentThread().getContextClassLoader()); } } catch (Exception e) { e.printStackTrace(); } } }, "线程 2").start(); } } ``` |
2
zxlzy 2021-12-09 11:46:56 +08:00
代码贴错了
public class Main { public static void main(String[] args) { Thread mainThread = Thread.currentThread(); new Thread(new Runnable() { @Override public void run() { try { ClassLoader cl = new ClassLoader() { @Override public String getName() { return "MyCloassLoader"; } }; TimeUnit.SECONDS.sleep(3); mainThread.setContextClassLoader(cl); System.out.println("在" + Thread.currentThread().getName() + "设置了主线程的自定义 classLoader " + cl); } catch (Exception e) { e.printStackTrace(); } } }, "线程 1").start(); new Thread(new Runnable() { @Override public void run() { try { for (int i = 0; i < 100; i++) { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "上下文 设置前的 classloader" + Thread.currentThread().getContextClassLoader()); } } catch (Exception e) { e.printStackTrace(); } } }, "线程 2").start(); } } |
3
BBCCBB 2021-12-09 11:53:17 +08:00
你线程 2 不是线程 1 的子线程主要是 ==
|
4
linuxsteam OP @zxlzy 明白了 谢谢大佬。谢谢谢谢谢~~~~~~。
项目中结果就是大佬说的情况 请问大佬,用户可以自定义多个 classloader 吗?就是 set 上下文加载器的时候 不把上次设置的覆盖。 我测试是没啥办法,这方面资料 搜索引擎都不好搜、、。。 |
6
zxlzy 2021-12-09 14:54:31 +08:00
@linuxsteam 首先你为什么要这样做呢。就算你不覆盖上次的,那你具体加载类的时候,还是只能用一个类加载器加载呀。
Class.forName 是可以传类加载器的。forName(String name, boolean initialize, ClassLoader loader)。 |
8
linuxsteam OP @zxlzy 工作中的项目,是引入外部 jar 包插件。每引入一次插件就得创建一次 ClassLoader
因为 UrlClassLoader 除了构造,不支持修改 URL 属性 [是固定长度数组] ; 我现在只有尝试自己实现类似 URLClassLoader 的东西。然后修改这个 ClassLoader 引用中的 URL 属性。(感觉源码 URL 属性用数组是有道理的,我用集合代替 感觉多半不行) |
9
zxlzy 2021-12-09 17:14:21 +08:00
“每引入一次插件就得创建一次 ClassLoader”,这个有什么问题呢,你是担心这个操作影响性能?所以不想每次都创建新的 ClassLoader?
|
10
linuxsteam OP @zxlzy 这个没担心,创建完就得 setContextClassloader 呀
set 完 以后 之前的 ContextClassloader 就会被覆盖了 |
11
pursuer 2021-12-09 19:43:35 +08:00
JVM 链接查找类时的 ClassLoader 和 ContextClassloader 好像是无关的。如果想实现动态增删的 ClassLoader ,可以通过覆写 findClass 实现。也可以参考下面这个,这是我一个小项目里的一个支持动态增减 ClassLoader 的类加载器。
https://github.com/Pursuer2/xplatj/blob/master/commonj/src/main/java/xplatj/javaplat/pursuer/lang/IntegratedClassLoader.java |
12
jorneyr 2021-12-09 22:01:52 +08:00
setContextClassLoader 主要是解决 bootstrap 加载的类能使用 SPI 加载用户指定的 jar 包,一般要先备份 classloader ,然后设置 context class loader ,使用完后恢复线程开始的 class loader
|