能不能留一下宝贵的建议或指导或者内推一下,现在帖中不方便放个人的简历。希望您能留下邮箱或者微信,我把简历发给您。我在这先简单介绍一下自己。
某高考大省一本计算机软件,工作三年以上。目前在杭州一家公司工作 3 年整,主要从事 Java 开发,现在是所在公司技术核心(技术 leader 加主程)。 领导信任,同事融洽,工作轻松。但是去阿里一直是自己的目标,外加以此做出的准备和付出的努力,以及考虑到的未来发展。
我的最大优点就是觉悟高(只有努力一条路可走),外加行动力还行。毕业以来每天都学习 1-2 小时,几乎没怎么间断。从本公司的代码看起, 直到完全理解架构、业务、实现,再到读技术书籍各大网站技术博客,再到深入 Java 核心 API 的源码。公司就这百万用户,年流水不过亿,任我这么折腾,但是平台就这么大。 虽然一个大企业技术不是一个人,但是这就经验和平台。我深刻认识到这一点,经验和眼界不足,我就疯狂研究源码,从 JCF 、JUC 到 Spring 全家桶、Mybatis 、再到 Netty 。
感慨 Java 程序员要学的太多,不说计算机、后端等相关知识,就是 Java 相关知识都特别庞大。记忆力不强只能一遍又一遍硬学,JUC 都学了十几遍,不过每次都有不同的收获也算值得。
但是自己三年多没面试过,虽然工作不重,但还是有工作要做,不知道要准备成什么样子,该刷面试题,还是要仔细复习一遍,还是先去其他公司面一下。
现在第一志愿就是杭州阿里,期望职位是阿里云、天猫、菜鸟的 Java 开发,给自己的定位是 P6 或 P6+。我知道人生七分天注定,但是也要把剩下的做好,把在自己身上出纰漏的概率降到最低。毕竟这是我的自荐,可能有些夸大的成分,如果有冒犯到大家,请不要生气。
重要的类仔细研究源码实现,比如 ThreadPoolExecutor 、ForkJoinPool 、AQS,特别重要的类一行一行研究源码,力保每行代码都知道是什么意思,为什么要这样设计,比如 ArrayList,ConcurrentHashMap 。但是认知有局限性,所以可能出现一些偏差,如果愿意指正,感激不尽。
ClassLoader,Java 8 只有 BootstrapClassLoader ( BCL )特殊,只有 BCL 有权力加载 java.包下的类,但是有非标准参数-Xbootclasspath/p 可以使自定义类取代 Java 核心 API, 比如 HashMap 之类。虽然 Java 层面无法获得 BCL 的引用,但是 Unsafe 的 defineClass 可以在 JVM 启动以后,也能让 BCL 加载自定义类,比如 java.test.JavaTest,但是无法替换核心 API 。ClassLoader 在 Java 11 (其实 Java 9 )发生了重大变化,-Xbootclasspath/p 被删除,核心 API 更加安全,ExtClassLoader 被删除,新增 PlatformClassLoader ( PCL ),当然不是换名这么简单。PCL 也有权力加载 java.包下的类,比如 module java.sql 就是其加载的。所谓的双亲委派模型也被抛弃了,现在 ClassLoader 加载的是 module 下的类,所以现在是先搜索类是否在当前 ClassLoader 加载的 module 下,然后交给父辈加载器,最后找不到会调用 SystemClassLoader 尝试加载 classpath 下的类(兼容 Java 9 之前的代码)。Tomcat 、Spring Boot 的类加载也不算复杂,Tomcat 广为流传的三个 common 、server 、shared 加载器其实都是 URLClassLoader 的实例,而且 7 及以后的版本这三个合而为一,只剩下 common 加载 lib,而内嵌情况下只有 WebappClassLoader 。Spring Boot 则扩展了 URLClassLoader 以支持 FatJar(jar in jar)。
类卸载条件苛刻的多,Java 规范规定 BCL 加载的类不能被卸载,而 Class 和加载其的 ClassLoader 实例又相互持有,所以绝大多数 Class 不会被卸载,包括动态代理生成的。但是 defineAnonymousClass ( lambda 原理)生成的类不会被其 ClassLoader 实例记录,还有反射创建的 Method 对象,调用一定次数会生成 Class,但是其 ClassLoader 实例只加载这一个类,所以这几种情况类被卸载还是不难的。
ThreadLocal,ThreadLocal 只是一个 Key,起作用的是 ThreadLocalMap,其三种节点 null 、key=null 、正常节点,其中 run ( between two null slots ),每次 get/remove 如果在查找元素的时候发现 key=null 节点,则清理并 rehash 从定位到 runEnd 的所有节点,而 set 查找位置的过程中遇到 key=null 节点则会清理整个 run 。而扩容则是达到 2/3 阈值之后清理整个数组,如果清理过后元素数量还大于容量的一半则扩容。针对如此复杂并且低效的 ThreadLocal,Netty 提供了 FastThreadLocal 取代之,底层使用数组加索引直接访问,用空间换时间,自下而上的改造让 FastThreadLocal 不会出现内存泄露。
ConcurrentHashMap ( CHM ),CHM 在 Java 8 中直接重写,现在几乎是个并行 Map,是 JUC 最重要的类,原理简单,设计巧妙。理解 CHM 的 bin 意义、计数、扩容、遍历就能大致能弄清楚它。CHM 计数基本上照搬了 LongAdder,CHM 的 size 能超过 threshold 吗?能超过 capacity 吗? addCount(long x, int check)的 check 如何理解,为什么要加 mappingCount()取代 size()呢。CHM 怎么保证在扩容过程中遍历的呢。CHM 最有技巧的是并发扩容,此时 sizeCtl 高 16 是数组长度的标识,低 16 位代表扩容线程+1,通过数组分段实现并发(并行)扩容,不可谓不巧妙。但是 Java 8 扩容的时候是有 bug,这个直到 12 才修复。而且在扩容的过程中,有两点我不敢苟同,1.对单核处理器(逻辑)只有一个线程完成扩容,2.这个更重要,所有数据迁移完毕后,留一个线程 recheck,在这个线程没有 commit 之前,所有写线程都会自旋,recheck 时间依赖数组大小。线程的运行时间由 OS 调度,OS 不可能让一个线程一直运行,所以在两种情况下,效率不高,也会造成很大的 CPU 时间浪费。
AQS,虽然现在 AQS 及其子类已经不算特别重要了,但是其设计思想还是值得学习的。AQS head 节点代表的意义是什么,为什么从 tail 向 head 查找,独占模式和共享模式除了 Condition 只能在独占模式使用外真正的区别到底是什么呢,是独占模式只能有一个线程获取同步状态吗,那能不能用独占模式写一个 Semaphore 呢。带着疑问学习 AQS,理解也会更加深刻。
ThreadPoolExecutor 、ForkJoinPool 、Netty EventExecutor/EventLoop,ThreadPoolExecutor 基于 BlockingQueue,正所谓成也萧何败也萧何,BlockingQueue 基于 AQS,而 AQS 实现的独占锁现在性能和 sychornized 差了不止一个数量级,不仅如此,底层共享一个 BlockingQueue,造成效率低下。ForkJoinPool 是个先进的线程池,不仅每个线程都有自己的工作队列,还能在空闲时窃取其他的任务,已到达负载均衡,它是 JDK 中最重要的并持续维护的线程池,是众多并发并行结构的底层线程池,如 CHM 的各种 TASK 、CompletableFuture 、parallelStream 等等。但是其依然有缺点,实现非常的复杂,它就是 JUC 最复杂的类(体系),而且每个版本都有大改动,行为在每个版本也不一样,负载均衡如此复杂却没带来高效,如果是 Java 8 则不建议使用,因为 ForkJoinPool 会创建和销毁大量的线程,commonPool 更加不推荐使用。而 Netty 线程池体系非常庞大,几十个类构成,实现了 Future/Promise 模型,EventLoop 绑定一个线程,每个 EventLoop 都有一个工作队列,并且通过轮询实现了任务提交的负载均衡( ForkJoinPool 是执行的负载均衡,思想更先进),Netty 的线程模型和线程池给人巨大的启发,线程并不需要太多,依然可以有极高的效率,Netty 5 就是打算使用 ForkJoinPool 取代其本身的线程池,但是最终效果并不好,此版本也废弃了(还有其他原因)。我目前知道 Netty 线程池唯一坑点就是 EventLoop 被关闭后不会重建,除了 NioEventLoop,其他的线程池都会被 execute 提交的 runnable 产生的 unchecked exceptions 关闭(大坑)。
当时好奇 Mybatis ( JDBC )增删查改的发送和返回到底是什么,用抓包工具分析 MySQL 的包(对应 OkPacket ),做了大量的测试,发现了没有开启 rewriteBatchedStatements 的 addBatch 就是循环提交,发现了更新操作的 Affected Rows 和 Rows matched 的区别,也发现了自增 id 和 Last_INSERT_ID 、addBatch 返回 ids 等线程安全的原理,原来这些是基于 MySQL session/JDBC connection,相当于 ThreadLocal,当然没有线程安全的影响。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.