并发编程网 - ifeve.com

让天下没有难学的技术

深入浅出ClassLoader

Dedicate to Molly.

你真的了解ClassLoader吗?

这篇文章翻译自zeroturnaround.com的 Do You Really Get Classloaders? ,融入和补充了笔者的一些实践、经验和样例。本文的例子比原文更加具有实际意义,文字内容也更充沛一些,非常感谢作者 Jevgeni Kabanov 能够共享如此优秀的文档。

1. 为什么你需要了解和敬畏ClassLoader

ClassLoader在Java语言中占据了核心地位,Java应用服务器,OSGi,以及大量的网络框架,它们大多数都用到了ClassLoader。如果在使用过程中出现了类加载错误,你能解决它吗?

我们将从JVM和开发者两个角度讲述ClassLoader,将会选择一些典型的案例,然后演示如何解决它们。NoClassDefFoundError,LinkageError等很多错误都会有特定的表征,我们分析每个例子,然后进行解决。

2. 进入ClassLoader

每个ClassLoader对象都是一个java.lang.ClassLoader的实例。每个Class对象都被这些ClassLoader对象所加载,通过继承java.lang.ClassLoader可以扩展出自定义ClassLoader,并使用这些自定义的ClassLoader对类进行加载。

先大体了解一下ClassLoader的API:

01 package java.lang;
02  
03 public abstract class ClassLoader {
04  public Class loadClass(String name);
05  
06  protected Class defineClass(byte[] b);
07  
08  public URL getResource(String name);
09  
10  public Enumeration getResources(String name);
11  
12  public ClassLoader getParent();
13 }

最重要的是ClassLoader的loadClass方法,它接受一个全类名,然后返回一个Class类型的实例。

defineClass方法接受一组字节,然后将其具体化为一个Class类型实例,它一般从磁盘上加载一个文件,然后将文件的字节传递给JVM,通过JVM(native 方法)对于Class的定义,将其具体化,实例化为一个Class类型实例。

getParent方法返回其parent ClassLoader。

getResourcegetResources方法,从给定的repository中查找URLs,同时它们也具备类似loadClass一样的代理机制,我们可以将loadClass视为:defineClass(getResource(name).getBytes())

Java由于其晚绑定和“解释型”的特性,类型的加载是到最晚才进行,一个类型直到被调用构造函数、静态方法或者在字段上使用时才会被加载。

考虑如下代码:

1 public class A {
2      public void doSomething() {
3           B b = new B();
4           b.doSomethingElse();
5      }
6 }

代码:B b = new B();等同于B b = Class.forName(“B”, false, A.class.getClassLoader()).newInstance();

这代表着,在类型A中使用到的类型,将由加载了类型A的类加载器来进行加载。

3. ClassLoader继承体系

当启动一个JVM时,bootstrap 类加载器就会加载java的核心类,例如:rt.jar中的类。bootstrap 类加载器是其他类加载器的parent,它使唯一一个没有parent的类加载器。

接下来是extension 类加载器,它以bootstrap 类加载器作为parent,它用来从Java系统变量java.ext.dir中的jar包中加载类的。

第三个,也是最重要的一个就是开发者使用的system classpath 类加载器 。它是extension 类加载器 的child,它用来从Java系统变量java.class.path下面加载类,可以通过 -classpath 来指定这个位置。

注意类加载器的体系并不是“继承”体系,而是一个“委派”体系。大多数类加载器首先会到自己的parent中查找类或者资源,如果找不到,才会在自己的本地进行查找。事实上,类加载器被定义加载哪些在parent中无法加载到的类,这样在较高层级的类加载器上的类型能够被“赋值”为较低类加载器加载的类型。

类加载器的委托行为动机是为了避免相同的类被加载多次。回到1995年,Java的主要方向被放在Applet上,那时候网络带宽优先,所以程序中的类直到用时才会被加载。但是事实上,Java在服务器端展示了强劲的能力,但是服务器端要求类加载器能够反转委派原则,也就是先加载本地的类,如果加载不到,再到parent中加载。

JavaEE的 委派模型

每个方块都是一个类加载器,JavaEE规范推荐每个模块的类加载器先加载本类加载的内容,如果加载不到才回到parent类加载器中尝试加载。

反转委派原则的原因是应用服务器中所携带的类库并不是应用所期待的,也许不适合应用开发者,一个常见的例子就是log4j的依赖在容器和不同的应用中都存在,但是它们的版本大都不同。

Tomcat的 类加载顺序(开启了delegate模式)

在Tomcat中,默认的行为是先尝试在Bootstrap和Extension中进行类型加载,如果加载不到则在WebappClassLoader中进行加载,如果还是找不到则在Common中进行查找。在Alibaba使用的Tomcat开启了delegate模式,因此加载类型时会以parent类加载器优先。

4. NoClassDefFoundError

NoClassDefFoundError是在开发JavaEE程序中常见的一种问题。该问题会随着你所使用的JavaEE中间件环境的复杂度以及应用本身的体量变得更加复杂,尤其是现在的JavaEE服务器具有大量的类加载器。

在JavaDoc中对NoClassDefFoundError的产生是由于JVM或者类加载器实例尝试加载类型的定义,但是该定义却没有找到,影响了执行路径。换句话说,在编译时这个类是能够被找到的,但是在执行时却没有找到。

这一刻IDE是没有出错提醒的,但是在运行时却出现了错误。

看看如下示例:

01 /**
02  * @author weipeng2k 2015年3月27日 下午5:15:15
03  */
04 @WebServlet(name = "NoClassDefFoundErrorServlet", urlPatterns = "/noClassDefFoundError.do")
05 public class NoClassDefFoundErrorServlet extends HttpServlet {
06  
07     private static final long serialVersionUID = 61585757018374721L;
08  
09     @Override
10     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
11         resp.getWriter().println(TestCase.class.toString());
12     }
13 }

在看pom.xml中对于依赖的定义:

01 <dependencies>
02     <dependency>
03         <groupId>junit</groupId>
04         <artifactId>junit</artifactId>
05         <version>3.8.1</version>
06         <scope>provided</scope>
07     </dependency>
08     <dependency>
09         <groupId>javax.servlet</groupId>
10         <artifactId>servlet-api</artifactId>
11         <version>3.0</version>
12         <scope>provided</scope>
13     </dependency>
14     <dependency>
15         <groupId>org.springframework</groupId>
16         <artifactId>spring</artifactId>
17         <version>2.5.6</version>
18     </dependency>
19 </dependencies>

其中对于junit的依赖是provided级别的,这里是为了能简化错误出现的条件。可以看到,在NoClassDefFoundErrorServlet中,使用了junit.jar中的TestCase,但是junit.jar在WEB-INF/lib中却没有,从而导致WebappClassLoader在进行加载TestCase时无法找到,从而抛出NoClassDefFoundError。我们需要从最终的war包中确定是否存在这个类,而不是在IDE中进行搜索。

5. NoSuchMethodError

在另一个场景中,我们可能遇到了另一个错误,也就是NoSuchMethodError。

NoSuchMethodError代表这个类型确实存在,但是一个不正确的版本被加载了。为了解决这个问题我们可以使用 ‘-verbose:class’ 来判断该JVM加载的到底是哪个版本。

看如下示例:

01 import org.springframework.beans.factory.BeanFactoryUtils;
02  
03 /**
04  * @author weipeng2k 2015年3月31日 上午9:09:58
05  */
06 @WebServlet(name = "NoSuchMethodErrorServlet", urlPatterns = { "/noSuchMethodError.do" })
07 public class NoSuchMethodErrorServlet extends HttpServlet {
08  
09     private static final long serialVersionUID = 1699609060417354821L;
10  
11     @Override
12     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
13         BeanFactoryUtils.isGeneratedBeanName("xxx");
14  
15         resp.getWriter().println("done.");
16     }
17 }

在doGet方法中调用了BeanFactoryUtils.isGeneratedBeanName(”xxx“);,看一下项目的pom依赖。

01 <dependencies>
02     <dependency>
03         <groupId>junit</groupId>
04         <artifactId>junit</artifactId>
05         <version>4.11</version>
06         <scope>provided</scope>
07     </dependency>
08     <dependency>
09         <groupId>javax.servlet</groupId>
10         <artifactId>servlet-api</artifactId>
11         <version>3.0</version>
12         <scope>provided</scope>
13     </dependency>
14     <dependency>
15         <groupId>org.springframework</groupId>
16         <artifactId>org.springframework.context</artifactId>
17         <version>3.0.5.RELEASE</version>
18         <scope>provided</scope>
19     </dependency>
20     <dependency>
21         <groupId>org.apache.mina</groupId>
22         <artifactId>mina-core</artifactId>
23         <version>2.0.7</version>
24     </dependency>
25     <dependency>
26         <groupId>com.alibaba.external</groupId>
27         <artifactId>sourceforge.spring</artifactId>
28         <version>2.0.7</version>
29     </dependency>
30 </dependencies>

这里为了方便观察到结果,将org.springframework.context的 scope 改为了 provided ,目的是不将其打包入war包,而只是使用了sourceforge.spring中定义的2.0.7版本,这个版本肯定没有isGeneratedBeanName(String name)方法,但是在IDE中,由于应用依赖到了高版本的spring从而能够编译通过,但是在运行时却没有那么好运了。这种错误,常见于 Maven坐标 的变动,使得应用依赖了多个 相同内容,不同版本 的jar包,以致在运行时选择了非期望的版本。

6. ClassCastException

NoClassDefFoundError和NoSuchMethodError是两个在 JavaEE 环境中经常出现的问题,这些问题需要 开发人员了解问题的本质,才能够被 从容 的处理。

下面我们看一下ClassCastException,在一个类加载器的情况下,一般出现这种错误都会是在转型操作时,比如:A a = (A) method();,很容易判断出来method()方法返回的类型不是类型A,但是在 JavaEE 多个类加载器的环境下就会出现一些难以定位的情况。

看如下示例:

01 package com.murdock.classloader.servlet;
02  
03 import java.io.File;
04 import java.io.IOException;
05 import java.net.URL;
06  
07 import javax.servlet.ServletException;
08 import javax.servlet.annotation.WebServlet;
09 import javax.servlet.http.HttpServlet;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12  
13 import org.apache.mina.proxy.utils.MD4;
14  
15 import com.murdock.classloader.CachedClassLoader;
16  
17 /**
18  * @author weipeng2k 2015年4月4日 下午6:00:54
19  */
20 @WebServlet(name = "ClassCastExceptionServlet", urlPatterns = "/classCastException.do")
21 public class ClassCastExceptionServlet extends HttpServlet {
22     private static final long   serialVersionUID    = -8959000121057369987L;
23  
24     @Override
25     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
26         String localFirst = req.getParameter("localFirst");
27         CachedClassLoader cl = null;
28         cl = new CachedClassLoader(
29                 new URL[] { new File(
30                         "/Users/weipeng2k/.m2/repository/org/apache/mina/mina-core/2.0.7/mina-core-2.0.7.jar").toURI()
31                         .toURL() }, this.getClass().getClassLoader());
32         if ("false".equals(localFirst)) {
33             cl.setLocalFirst(false);
34         }
35         try {
36             Class<?> klass = cl.loadClass("org.apache.mina.proxy.utils.MD4");
37             MD4 md4 = (MD4) klass.newInstance();
38  
39             resp.getWriter().println(md4);
40  
41         catch (Exception ex) {
42             throw new RuntimeException(ex);
43         finally {
44             cl.close();
45         }
46  
47     }
48 }

在ClassCastExceptionServlet中,构建了一个CachedClassLoader,利用这个ClassLoader加载org.apache.mina.proxy.utils.MD4,然后反射调用构造该类的实例,将其赋给MD4,最后将其打印到浏览器。

请求URLhttp://localhost:8080/classCastException.do

响应页面,出现错误:

1 java.lang.RuntimeException: java.lang.ClassCastException: org.apache.mina.proxy.utils.MD4 cannot be cast to org.apache.mina.proxy.utils.MD4
2     com.murdock.classloader.servlet.ClassCastExceptionServlet.doGet(ClassCastExceptionServlet.java:42)
3     javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
4     javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
5     org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

请求URL :http://localhost:8080/classCastException.do?localFirst=false
响应页面,输出正常:

1 org.apache.mina.proxy.utils.MD4@401c8af5

请求的URL加上了localFirst=false就可以正常的输出,而它也就是在CachedClassLoder上设置了一下,为什么有这么大的差别。org.apache.mina.proxy.utils.MD4全类名一致,为什么会出现ClassCastException呢?

在JVM中,如何确定一个类型实例?答:全类名吗?不是,是类加载器加上全类名。在JVM中,类型被定义在一个叫SystemDictionary 的数据结构中,该数据结构接受类加载器和全类名作为参数,返回类型实例。

SystemDictionary 如图所示:

类型加载时,需要传入类加载器和需要加载的全类名,如果在 SystemDictionary 中能够命中一条记录,则返回class 列上对应的类型实例引用,如果无法命中记录,则会调用loader.loadClass(name);进行类型加载。

这里不会更加深入的介绍 SystemDictionary 如何进行类型加载的过程,而是需要指出 JVM中确定一个类型的坐标是通过类加载器和全类名做到的 。回想一下MD4 md4 = (MD4) klass.newInstance();,是不是代表着等式两边的MD4是不同的类加载器加载的呢?那问题一定出在 CachedClassLoader 上。这里贴一下loadClass(String name)方法的部分逻辑。

CachedClassLoader 的loadClass逻辑:

01 if (localFirst) {
02     try {
03         clazz = findClass(name);
04         if (clazz != null) {
05             return clazz;
06         }
07     catch (ClassNotFoundException ex) {
08  
09     }
10     return super.loadClass(name);
11 else {
12     return super.loadClass(name);
13 }

可以看到在 localFirst 为true时,该类加载器会首先加载自身 repository 中的类型,如果加载不到,则会尝试默认的加载机制进行加载,也就是parent优先加载。这样就可以解释MD4 md4 = (MD4) klass.newInstance();,等式左边MD4 md4,这个类型是WebappClassLoader.org.apache.mina.proxy.utils.MD4,等式右边klass.newInstance()返回的类型是CachedClassLoader.org.apache.mina.proxy.utils.MD4,二者并不是同一个类型,所以无法完成类型转换,最终抛出 ClassCastException 。而当 localFirst 为false时,该类加载器遵循parent优先,从而会先委派给WebappClassLoader进行加载,当然转型也就不会有问题了。

在传统的双亲委派模型下,这种 ClassCastException 是不会发生的,因为它的加载顺序杜绝了出现这种问题的可能,而在 JavaEE 环境下,每个资源模块(比如一个war包)都优先使用自身的资源,正因为突破了双亲委派模型, 奇怪的问题 就发生了。

7. LinkageError

有时候事情会变得更糟,和 ClassCastException 本质一样,加载自不同位置的*相同类*在同一段逻辑(比如:方法)中交互时,会出现 LinkageError 。

我们先看一下出错的异常信息,然后分析一下它产生的条件和原因:

01 java.lang.LinkageError: loader constraint violation: when resolving overridden method"com.murdock.classloader.linkageerror.Param2.generate()Lcom/murdock/classloader/linkageerror/Param2;" theclass loader (instance of com/murdock/classloader/linkageerror/LinkageErrorTest$1) of the current class, com/murdock/classloader/linkageerror/Param2, and its superclass loader (instance of sun/misc/Launcher$AppClassLoader), have different Class objects for the type com/murdock/classloader/linkageerror/Param2 used in the signature
02     at java.lang.Class.getDeclaredConstructors0(Native Method)
03     at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
04     at java.lang.Class.getConstructor0(Class.java:3075)
05     at java.lang.Class.newInstance(Class.java:412)
06     at com.murdock.classloader.linkageerror.LinkageErrorTest.test(LinkageErrorTest.java:34)
07     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
08     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
09     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
10     at java.lang.reflect.Method.invoke(Method.java:497)
11     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
12     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
13     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
14     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

看到一堆出错信息,但是不要紧张,慢慢的读一下出错信息,这种错误一般会让你直觉感觉不会出现。loader constraint violation表示类加载器冲突了,这句话暗示: 相同的类,由不同的ClassLoader加载,但是在这里遇到了when resolving overridden method "com.murdock.classloader.linkageerror.Param2.generate()Lcom/murdock/classloader/linkageerror/Param2;"表示在解析那条语句出现了问题,这里表示在Param2.generate()方法的解析过程中出现了问题。the class loader (instance of com/murdock/classloader/linkageerror/LinkageErrorTest$1) of the current class, com/murdock/classloader/linkageerror/Param2,表示解析的语句所在的类型Param2LinkageErrorTest$1类加载器加载的。and its superclass loader (instance of sun/misc/Launcher$AppClassLoader), have different Class objects for the type com/murdock/classloader/linkageerror/Param2 used in the signature表示Param2的超类Param中被覆盖的方法返回的类型Param2Launcher$AppClassLoader加载。

Linkage在常规情况下非常难以制造,只有在多个类加载器交互时才有可能出现,下面看一下问题代码。出现问题的类和参数:

01 package com.murdock.classloader.linkageerror;
02  
03 /**
04  * @author weipeng2k 2015年4月28日 上午10:04:26
05  */
06 public class HandleUtils {
07     public void m(Param param) {
08         param.generate();
09     }
10  
11 }
12  
13 package com.murdock.classloader.linkageerror;
14  
15 public class Param {
16     public Param2 generate() {
17         return new Param2();
18     }
19 }
20  
21 package com.murdock.classloader.linkageerror;
22  
23 public class Param2 extends Param {
24     public Param2 generate() {
25         return new Param2();
26     }
27 }

测试用例如下:

01 @Test
02 public void test() throws Exception {
03  
04     // cl1在加载HandleUtils和Param时将会使用AppClassLoader
05     URLClassLoader cl1 = new URLClassLoader(new URL[] {new File("target/test-classes").toURI().toURL()}, null) {
06  
07         @Override
08         public Class<?> loadClass(String name) throws ClassNotFoundException {
09             if ("com.murdock.classloader.linkageerror.HandleUtils".equals(name)) {
10                 return ClassLoader.getSystemClassLoader().loadClass(name);
11             }
12  
13             if ("com.murdock.classloader.linkageerror.Param".equals(name)) {
14                 return ClassLoader.getSystemClassLoader().loadClass(name);
15             }
16  
17             return super.loadClass(name);
18         }
19  
20     };
21  
22     ClassLoader.getSystemClassLoader().loadClass("com.murdock.classloader.linkageerror.Param2");
23     HandleUtils hu = (HandleUtils) cl1.loadClass("com.murdock.classloader.linkageerror.HandleUtils").newInstance();
24     hu.m((Param) cl1.loadClass("com.murdock.classloader.linkageerror.Param2").newInstance());
25 }

LinkageError 需要观察哪个类被不同的类加载器加载了,在哪个方法或者调用处发生(交汇)的,然后才能想解决方法,解决方法无外乎两种。第一,还是不同的类加载器加载,但是相互不再交汇影响,这里需要针对发生问题的地方做一些改动,比如更换实现方式,避免出现上述问题;第二,冲突的类需要由一个Parent类加载器进行加载。**LinkageError** 和**ClassCastException** 本质是一样的,加载自不同类加载器的类型,在同一个类的方法或者调用中出现,如果有转型操作那么就会抛 ClassCastException ,如果是直接的方法调用处的参数或者返回值解析,那么就会产生 LinkageError 。

8. 类加载器问题对照表

遇到类加载器问题时,可以尝试使用下面的表格进行问题排查。

类找不到 了不正确的类 多于一个类被加
ClassNotFoundException NoClassDefFoundError IncompatibleClassChangeError NoSuchMethodError NoSuchFieldError IllegalAccessError ClassCastException LinkageError
IDE class lookup (Ctrl+Shift+T in Eclipse)find . -name “*.jar” -exec jar -tf {} \; | grep DateUtils

使用middleware-detector

通过在启动参数中加 -verbose:class,观察加载的类来自哪个jar包使用middelware-detector 通过`-verbose:class`观察

9. 使用Middleware-Detector进行类查找

出现了 ClassNotFoundException 或者 NoClassDefFoundError ,需要检查一下程序的classpath下面是否存在你所预想的类。这时可以使用Middleware-Detector工具进行类查找,该工具是Alibaba中间件团队开发的一款中间件问题诊断工具,当然也包括了许多支持性质的工具。

下面我们使用Middleware-Detector进行类查找,比如我们要查找apache的Utils,我们怀疑这个类在classpath下找不到。

启动middleware-detector,查看 Pandora 提供的自定义检查器,目前编号为1的Pandora自定义检查器就是进行classpath下的指定类或者接口的查找工作。

配置classpath目录以及需要查找的类名,这里类名支持 * 号进行模糊匹配。可以看到设定当前的classpath目录到了WEB-INF/lib 下面,然后找寻*apache*comm*A*Utils是否存在,如果能够找到则会输出到终端,这里就找到了ArchiveUtils和ArrayUtils两个符合要求的类。如果无法找到,那么就可能是pom.xml的依赖配置不正确了,需要检查一下。

10. 使用Middleware-Detector进行检查类冲突

出现了 NoSuchMethodError 或者 NoSuchFieldError ,这时一般是应用的classpath下包含了多个包含了想同类的jar包,而很不幸的加载到了 不正确 的jar包。

我们可以通过使用Middleware-Detector的类查找进行定位,但是不能发现一个修复一个,这里Middleware-Detector提供了一个检查classpath下有冲突jar包的功能。只需要设置classpath的目录,然后运行cc –check tomcat#1即可。有冲突的jar就需要自己在pom.xml里面进行仲裁或者排除了。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 深入浅出ClassLoader

并发编程网 - ifeve.com的更多相关文章

  1. Java内存模型---并发编程网 - ifeve.com

    Java内存模型 转自:http://ifeve.com/java-memory-model-6/ 原文地址  作者:Jakob Jenkov 译者:张坤 Java内存模型规范了Java虚拟机与计算机 ...

  2. JAVA工程师面试题【来自并发编程网】

    基础题: Java线程的状态 进程和线程的区别,进程间如何通讯,线程间如何通讯 HashMap的数据结构是什么?如何实现的.和HashTable,ConcurrentHashMap的区别 Cookie ...

  3. java并发编程学习:用 Semaphore (信号量)控制并发资源

    并发编程这方面以前关注得比较少,恶补一下,推荐一个好的网站:并发编程网 - ifeve.com,上面全是各种大牛原创或编译的并发编程文章. 今天先来学习Semaphore(信号量),字面上看,根本不知 ...

  4. 《C++ 并发编程》- 第1章 你好,C++的并发世界

    <C++ 并发编程>- 第1章 你好,C++的并发世界 转载自并发编程网 – ifeve.com 本文是<C++ 并发编程>的第一章,感谢人民邮电出版社授权并发编程网发表此文, ...

  5. java架构《并发编程框架篇 __Disruptor》

    Disruptor入门   获得Disruptor 可以通过Maven或者下载jar来安装Disruptor.只要把对应的jar放在Java classpath就可以了. 基本的事件生产和消费 我们从 ...

  6. java并发编程_建立概念

    在学习多线程编程时,相信大家会遇到好多概念类的东西,对于这些概念的不准确理解会导致后面越学越糊涂,现将学习过程中遇到的概念整理到这篇博客上,一来记录学习点滴,二来也加深理解,如果有理解不准确的地方,希 ...

  7. JAVA并发编程J.U.C学习总结

    前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...

  8. 《java并发编程实战》笔记

    <java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为:  Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...

  9. 【java并发编程实战】第八章:线程池的使用

    1.线程饥饿锁 定义:在线程池中,如果任务的执行依赖其他任务,那么可能会产生线程饥饿锁.尤其是单线程线程池. 示例: public class ThreadDeadStarveTest { publi ...

随机推荐

  1. Node.js:创建第一个应用

    ylbtech-Node.js:创建第一个应用 1.返回顶部 1. Node.js 创建第一个应用 如果我们使用PHP来编写后端的代码时,需要Apache 或者 Nginx 的HTTP 服务器,并配上 ...

  2. php添加文件到压缩文件夹

    extension=php_zip.dll注释掉 <?php$filename = "test.zip"; ob_end_clean(); $zip = new ZipArc ...

  3. HDU1412 {A} + {B}

    2019-05-17 10:15:01 每个元素之间有一个空格隔开. 每行最后一的元素后面没有空格,区别于HDU人见人爱A - B 注意使用STL的时候要清空 .  a.clear(); #inclu ...

  4. Excel 查找某列中的数据在另一列是否存在并输出其他列的数据

    最近在操作Excel文件数据导入数据库时,经常需要检查Excel中哪些数据数据库中已经存在,哪些不存在,然后再将不存在数据库中的Excel数据导入:在此过程中,经常需要操作Excel中的数据,所以.也 ...

  5. BZOJ 2073

    思路: 状压DP  枚举子集 //By SiriusRen #include <cstdio> #include <cstring> #include <algorith ...

  6. Svn install and use

    1.安装服务 使用yum安装subversion,简单.不繁琐. 1 yum install -y subversion 2.创建版本库 1 2       mkidr /svn/obj        ...

  7. Dynamic编程

    Dynamic Binding 动态绑定 Binding:解析Type,member,operation的过程. 动态绑定将Binding从编译时延迟到运行时进行. 场景 编译时,程序员知道特定的fu ...

  8. 使用Axis2方式发布webService实例说明

    1.简单的pojo方式: 不需要写配置文件,直接把class文件拷贝到axis2的WEB-INF目录下的poji文件夹下即可.但其局限性表现在,实现类不能有包声明,这在实际开发过程中使用较少,这里不做 ...

  9. SQLServer 事务的隔离级别

    SQLServer事务的隔离级别 数据库是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况. 更新丢失(Lost update) 两个事务都同时更新一行数据,但是第二个事务 ...

  10. (转) RabbitMQ学习之工作队列(java)

    http://blog.csdn.net/zhu_tianwei/article/details/40887717 参考:http://blog.csdn.NET/lmj623565791/artic ...