由初始化线程池引发的NoClassDefFoundError 异常分析

今天说的异常是一个很不常见的异常,至少我不经常见到这个异常。 首先先看下NoClassDefFoundError官方定义 : Java Virtual Machine is not able to find a particular class at runtime which was available at compile time. If a class was present during compile time but not available in java classpath during runtime. Java 虚拟机无法在运行时找到一个在编译时可用的特定类。如果在编译时存在类, 但在运行时 java 类路径中不可用。 最近做的一个项目,由同事到客户方部署及应用,但是期间发生一个诡异的问题:同一套代码打出的jar包在一个公司运行时会有一个NoClassDefFoundError异常抛出。起初看到这个异常,我们都认为是打得包或者依赖有问题。于是便重新打包部署,结果还是同样的问题。异常信息如下:

很诡异的问题,顺着报的错误去继续查找原因,最后将问题定位到一个线程池工具类中,部分代码如下: 其中 DEFAULT_MAX_CONCURRENT 定义如下:

private static final int DEFAULT_MAX_CONCURRENT = Runtime.getRuntime().availableProcessors() * 2;

这个线程池工具类在本地以及测试环境和线上环境一直都运行的没有问题,因为报错的异常信息指向了这个类。 考虑到在多个客户部署的都是同一套代码,只有硬件配置可能不同,而我们线程池初始化时的核心线程数依赖于硬件CPU核数,所以便猜测初始化线程池出了问题,核心线程数可能比最大线程数还大。 于是便开始追踪源码,一探究竟。 线程池初始化源码:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

继续往下看其初始化过程:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

上面代码可能看出,如果corePoolSize>maxPoolSize 则会抛出:IllegalArgumentException 异常,但是这和我们问题压根不一样啊?线索到这里就断了,但是至少发现了代码的一处Bug。 于是又开始沉思这个NoClassDefFoundError 异常究竟是怎么来的了,打开Oracle 文档便开始全局搜索这个,果不其然,有了新的发现: (文档地址:https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2)

这里意思是初始化过程时,如果这个类是用c去实现的,且初始化抛出异常时,都会对外抛出NoClassDefFoundError 异常,到了这里就很明朗了,果然是初始化线程池搞错了。 于是赶紧查看客户机器CPU核数来验证自己的猜想,果不其然,CPU为8核处理器。赶紧改了代码重新打包部署,一切到这里就结束了。 不过通过这次异常也学到了很多: 1,能不用硬编码的应该坚决杜绝,少埋这种坑。 2,多查文档,多查官方文档。 由于博主能力有限,所以如果您有更多的见解还请留言告知,不胜感激。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java帮帮-微信公众号-技术文章全总结

Java并发学习3【面试+工作】

ReadWriteLock是jdk5中提供的读写分离锁。读写分离锁可以有效的帮助减少锁竞争,以提升性能。用锁分离的机制来提升性能非常容易理解,比如线程A1,A2...

15240
来自专栏编舟记

Java高编译低运行错误(ConcurrentHashMap.keySet)

本地使用maven编译和运行时一切都正常,但是通过ci的方式,编译、打包、发布到部署环境,运行时抛出了一条显而易见的JDK版本的错误。

13730
来自专栏ImportSource

Java 并发编程系列: CountDownLatch (上厕所的案例)

1、What is CountDownLatch? CountDownLatch提供了一种同步机制,可以允许一个或多个线程等待,直到其他线程上的一系列操作完成后...

30260
来自专栏JavaEdge

JUC源码分析之CyclicBarrier简介关键方法与参数源码解析CountDownLatch和CyclicBarrier的区别与联系应用场景小结

36380
来自专栏java技术学习之道

java线程-看这一篇就够了

15930
来自专栏Java技术栈

史上最全 Java 多线程面试题及答案

这些多线程的问题,有些来源于各大网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也有、也可能有些各位网友也都看过,但是本文写作的重心就是所...

9310
来自专栏余林丰

12.ThreadPoolExecutor线程池原理及其execute方法

jdk1.7.0_79   对于线程池大部分人可能会用,也知道为什么用。无非就是任务需要异步执行,再者就是线程需要统一管理起来。对于从线程池中获取线程,大部分...

28270
来自专栏Janti

由浅入深理解Java线程池及线程池的如何使用

前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担。线程本身也要占用内存空间,大量的线程会占用内存资源...

4.8K90
来自专栏Java架构师历程

40个Java多线程问题总结

java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多、越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的...

16630
来自专栏Linyb极客之路

并发编程之线程池

一、关于ThreadPoolExecutor 为了更好地控制多线程,JDK提供了一套Executor框架,帮助开发人员有效的进行线程控制,其本质就是一个线程池。...

47080

扫码关注云+社区

领取腾讯云代金券