JAVA
文章平均质量分 86
黄智霖-blog
骨灰级技术爱好者
展开
-
SpringCloud系列之版本选择
SpringBoot版本 进入Spring官网,查看当前SpringBoot版本: 当前最新版本是2.5.4 GA,如果单纯使用SpringBoot框架,那么选择2.5.4 GA就行。GA即General Availability,表示官方推荐使用的稳定版本,和release版本一个意思~关于SpringBoot的更多版本可以在https://github.com/spring-projects/spring-boot/wiki#release-notes找到。SpringCloud原创 2021-09-09 15:53:27 · 9964 阅读 · 0 评论 -
理解Java常量池
完全理解Java常量池原创 2021-06-06 21:06:08 · 512 阅读 · 1 评论 -
Spring源码分析系列-循环依赖和三级缓存
循环依赖循环依赖(区别于dependsOn)的现象很简单,A对象在创建的时候依赖于B对象,而B对象在创建的时候依赖于A对象原创 2021-05-25 22:13:41 · 3566 阅读 · 9 评论 -
Java并发编程(二十):FutureTask源码分析
使用 FutureTask<String> futureTask = new FutureTask(() -> "success"); new Thread(futureTask).start(); futureTask.get();源码分析FutureTask提供了两个构造方法,分别是传入一个Callable,和传入一个Runnable和返回值result。如果传入的是Runnable和返回值,那么会通过适配器RunnableAdap原创 2021-05-11 01:07:16 · 2743 阅读 · 1 评论 -
Java并发编程(十九):ScheduledThreadPoolExecutor总结与源码分析
目录前言使用源码分析前言 ScheduledThreadPoolExecutor主要用于处理延时任务或者定时任务,在平时的工作场景中也有被广泛应用,而我们有了前文关于ThreadPoolExecutor源码的深入理解,这里理解ScheduledThreadPoolExecutor便会顺畅不少。使用ScheduledThreadPoolExecutor的使用还是比较简单,总体来说主要有三种方式提交任务:schedule:scheduleAtFixedRate:scheduleWithFixe原创 2021-05-09 00:56:59 · 8339 阅读 · 12 评论 -
Java并发编程(十八):ThreadPoolExecutor总结与源码深度分析
前言JUC中包含了很多的工具类,不论是第三方工具包,还是自己开发的业务系统,使用最多的还是是线程池,比如普通的线程池ThreadPoolExecutor、带调度的线程池ScheduledThreadPoolExecutor等,本文就从源码层面探究一下ThreadPoolExecutor的实现原理~注:研究线程池源码的目的是为了让自己能够对其有个清晰的认识,理解各个参数设置的含义,以帮助我们正确合理的使用线程池,除非时间非常充足,否则不建议去逐行研究其中的源码,本文也只是分析其核心逻辑的源码~总览原创 2021-04-28 23:01:19 · 3785 阅读 · 9 评论 -
Java并发编程(十六):CyclicBarrier源码分析
CyclicBarrier可以建立一个屏障,这个屏障可以阻塞一个线程直到指定的所有线程都达到屏障。就像团队聚餐,等所有人都到齐了再一起动筷子。根据Cyclic就可以发现CyclicBarrier可以重复使用。现在有了前面分析ReentrantLock、Semaphore、CountDownLatch的经验,CyclicBarrier也不复杂了,只是这里又引入新的概念:Condition条件队列,这也是最开始我们分析AQS没有讲的东西。原创 2021-04-21 00:23:33 · 4613 阅读 · 6 评论 -
Java并发编程(十五):CountDownLatch源码逐行深度分析
前言CountDownLatch维护了一个计数器(还是是state字段),调用countcountDown方法会将计数器减1,调用await方法会阻塞线程直到计数器变为0。可以用于实现一个线程等待所有子线程任务完成之后再继续执行的逻辑,也可以实现类似CyclicBarrier的功能,达到让多个线程等待同时开始执行某一段逻辑目的。有了前面Semaphore源码分析和ReentrantLock源码分析的基础,再来看CountDownLatch的源码就简单的多了。使用一个线程等待其它线程执行完再继续执行原创 2021-04-18 18:57:26 · 4258 阅读 · 7 评论 -
Java并发编程(十四):Semaphore源码逐行深度分析
使用Semaphore即信号量,可以控制并发访问特定的资源的线程的数量,比如可用于接口请求限流,和ReentrantLock一样依赖AQS实现,使用的话比较简单:Semaphore semaphore = new Semaphore(10);thread-0:semaphore.acquire();//do somethis......semaphore.release();thread-1:semaphore.acquire();//do somethis......semaphor原创 2021-04-14 21:18:03 · 4640 阅读 · 5 评论 -
Java并发编程(十三):ReentrantLock-tryLock(long timeout, TimeUnit unit)源码分析
在前文ReentrantLock-NonfairSync源码逐行深度分析中,已经分析了AQS加解锁,阻塞唤醒与CLH队列的使用,这里主要看看带超时时间的tryLock实现。在ReentrantLock的tryLock(timeout)方法中调用的是sync的tryAcquireNanos:public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return原创 2021-04-12 22:02:20 · 1970 阅读 · 0 评论 -
SPI之ServiceLoader应用与源码分析
在SPI的应用中,Java支持对外开放了一些服务接口(或者抽象类,也可以使用普通类,但是不建议),但是不提供具体的实现,这些实现可以由第三方提供,支持用户以扩展的方式集成到系统中。那么这就需要一种服务发现机制。也就是根据一定的规则到指定的位置找到指定服务的配置文件,然后找到实现类。ServiceLoader便提供了这么一个手段,能够在系统中"指定"位置(META-INF/services)的"指定"文件(文件名是服务全限定名称)中寻找指定服务的三方实现。原创 2021-04-12 22:38:46 · 4591 阅读 · 1 评论 -
Jetty对ServletContainerInitializer的支持与Spring的应用
关于ServletContainerInitializer和@HandlesTypesServletContainerInitializer是一个定义在javax.servlet包中的接口,根据servlet规范,框架提供的 ServletContainerInitializer实现必须绑定在 jar包的META-INF/services目录中的一个叫做 javax.servlet.ServletContainerInitializer的文件,在其中指定ServletContainerInitialize原创 2021-04-09 22:30:42 · 2942 阅读 · 0 评论 -
Java并发编程(十二):ReentrantLock-FairSync源码分析(hasQueuedPredecessors)
前言有了前面ReentrantLock-NonfairSync源码逐行深度分析的基础,理解公平锁FairSync的实现就很简单了。ReentrantLock的默认构造函数创建的是非公平锁,同时提供了布尔类型的有参构造函数,true表示创建公平锁。public ReentrantLock() { sync = new NonfairSync();}public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new No原创 2021-04-08 22:23:29 · 4515 阅读 · 1 评论 -
Java并发编程(十一):ReentrantLock-NonfairSync源码逐行深度分析(下)
前言前面ReentrantLock-NonfairSync源码逐行深度分析(上)和ReentrantLock-NonfairSync源码逐行深度分析(中)两文从源码层面深度分析了NonfairSync的逻辑,有了前面的基础,我们就算是向AQS迈出了第一步,本文针对前面两篇文章做一个系统总结,后面就继续分析依赖AQS的其它工具类。ReentrantLock关键点AQS是抽象类AbstractQueuedSynchronizer的缩写,ReentrantLock中定义了静态内部类Sync继承自AQS,并原创 2021-04-08 13:24:46 · 5260 阅读 · 7 评论 -
Java并发编程(十):ReentrantLock-NonfairSync源码逐行深度分析(中)
,同样在AQS中找到该方法的实现:final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { //标识是否被中断 boolean interrupted = false; //死循环 for (;;) { //获取node节点的前驱节点原创 2021-04-07 23:57:09 · 6176 阅读 · 7 评论 -
Java并发编程(九):ReentrantLock-NonfairSync源码逐行深度分析(上)
CAS 修改state加锁自旋加锁park阻塞线程队列保存阻塞的线程:双向链表实现队列,为了防止每次判空,会有一个空的头结点。CAS保证入队必须成功unpark或interrupt唤醒原创 2021-04-05 01:18:04 · 3757 阅读 · 4 评论 -
Java实用工具之StringJoiner
背景 在平时的业务开发中,我们可能会遇到字符串列表根据分隔符进行拼接的需求。比如:输入:数组:[“a”,“b”,“c”]分隔符:","输出:“a,b,c”处理 通常我们可以使用StringBuilder根据下标位置决定是否需要添加分隔符以达到目的,比如:public static void main(String[] args) { StringBuilder sb = new StringBuilder(); String[] string原创 2021-04-02 00:19:19 · 1330 阅读 · 0 评论 -
多类加载器环境下使用synchronized的注意事项
如果使用synchronized对obj.getClass()加锁,锁对象就是Class,那么对于这个Class下的所有实例对象访问都能达到同步控制的目的。比如以下代码:public class JavaMain { public void run() { synchronized (this.getClass()) { int i = 0; while (i++ < 10) { try {原创 2021-03-31 02:42:24 · 2313 阅读 · 4 评论 -
垃圾收集之Remember Set(CardTable)
JVM为了更好的管理内存,提高GC效率,一般都会对内存进行划分,比如经典的分代收集,G1推出的Region等。而Java中的垃圾收集基本都基于可达性分析算法,这就涉及到对象枚举遍历和标记的过程。在做Minor GC的时候会对年轻代进行根节点枚举,但是如果年轻代中的一些对象被老年代引用着,那么在做年轻代可达性分析的时候就会遇到很大的阻碍,总不能在Minor GC的时候还去扫描老年代吧?这种跨代引用的问题抛开出现频率,它总是会存在,而且在G1这种多Region结构中更加突出(包括ZGC、Shenandoah原创 2021-03-31 01:40:11 · 4327 阅读 · 3 评论 -
深入OpenJDK源码-偏向锁的延时生效如何实现的
关于偏向锁,包括撤销和重偏向在前面已经结合源码分析过了,这里就不再赘述,主要看看延时生效的问题。另外,本文贴出的源码大多都只保留了当前关注的逻辑。 我们知道虚拟机为我们提供了参数- XX:+UseBiasedLocking以开启或者关闭偏向锁优化(默认开启),但是偏向锁的启用有个默认的延时时间,可以通过参数- XX:BiasedLockingStartupDelay设置,默认为4秒,可以在globals.hpp中找到默认值设置: product(intx, BiasedLockingStartu.原创 2021-03-30 23:45:49 · 5688 阅读 · 4 评论 -
深入OpenJDK源码--你真的了解System.out.println吗?
目录一、前戏二、JVM源码分析三、坑?四、总结一、前戏 可能不少小伙伴习惯在代码中使用sout打印一些信息,就像这样:System.out.println("hello world!") 做为一位资深干码人,本着弘扬党求真务实的精神,必须得来看看这个sout有何玄机~~ 首先看调用就知道,out是System类的一个公共静态成员变量,进入System.java中:public final static PrintStream out = null; 嗯,不止是public,还是fi原创 2021-03-30 02:10:24 · 8160 阅读 · 16 评论 -
一个Spring+SpringMVC动态代理不生效的问题
有一个使用SSM搭建的服务,之前由于功能扩展需要引入AOP,但是测试的时候AOP却无法生效,通过检查最终确认了问题,还顺带送了另一个问题~原创 2021-03-30 02:08:27 · 1858 阅读 · 0 评论 -
可达性分析之三色标记算法详解
二、三色标记在前文中提到了,在CMS的并发清理阶段才产生的垃圾,会被当做浮动垃圾,会留到下一次GC再清理。其实在并发标记阶段,由于用户线程在并发运行,也就可能导致引用关系改变,导致标记结果不准确,从而引发更加严重的问题,这些发生变更的数据会在重新标记阶段被处理,那么会出现什么问题?又是如何处理的呢?CMS算法的基础是通过可达性分析找到存活的对象,然后给存活的对象打个标记,最终在清理的时候,如果一个对象没有任何标记,就表示这个对象不可达,需要被清理。并发标记阶段是从GC Root直接关联的对象开始枚举的原创 2021-03-30 02:11:16 · 12118 阅读 · 15 评论 -
垃圾收集器总结--CMS垃圾收集器
目录一、CMS1.1 概述1.2 内存碎片1.3 浮动垃圾1.4 空间预留1.4 Promotion Failed和Concurrent Mode Failure1.5 常用参数二、总结一、CMS1.1 概述 CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,其主要优点就是并发收集、低停顿。适合重视服务响应速度的应用的服务器,基于标记-清除算法,用于老年代。它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次基本上实现了让垃圾收集线程与原创 2021-03-28 22:49:46 · 3130 阅读 · 0 评论 -
Spring源码之动态代理实现简单介绍
这里不阐述Spring的一些基本概念和AOP如何使用,直接进入正题。Spring管理的对象大体会经过确定实例化对象、推断构造方法创建对象(实例化)、设置属性、初始化等等步骤。在确定实例化对象阶段,Spring为开发者提供了一个BeanPostProcessor接口,它会在对象初始化之前和初始化之后被调用(初始化,不是实例化,对应实例化的是InstantiationAwareBeanPostProcessor接口)。public interface BeanPostProcessor { //初始化之前原创 2021-03-27 17:23:29 · 6093 阅读 · 15 评论 -
《深入理解Java虚拟机》读书笔记(十)--晚期(运行期)优化(下)
目录一、公共子表达式消除二、数组边界检查消除三、隐式异常处理四、方法内联五、逃逸分析六、Java与C/C++的编译器对比一、公共子表达式消除 如果一个表达式E已经计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就成为公共子表达式。对于这种表达式,没有必要花时间再对它进行计算,只需要直接用前面计算过的结果代替E就行了。如果这种优化仅限于程序的基本块内,便称为局部公共子表达式消除,如果优化的范围涵盖了多个基本块,就称为全局公共子表达式消除。 对于以下代码:int d原创 2021-03-27 03:07:21 · 3830 阅读 · 4 评论 -
《深入理解Java虚拟机》读书笔记(十)--晚期(运行期)优化(上)
文章目录一、HotSpot虚拟机内部的即时编译器1.1 解释器和编译器1.2 编译对象和触发条件1.2.1 方法调用计数器1.2.2 回边计数器1.3 编译过程二、编译优化技术2.1 方法内联2.2 冗余访问消除2.3 复写传播2.4 无用代码消除一、HotSpot虚拟机内部的即时编译器1.1 解释器和编译器 当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获得更高的执行效率。原创 2021-03-25 01:47:55 · 5402 阅读 · 6 评论 -
Java中Collections.emptyList()注意
偶然发现有小伙伴错误地使用了Collections.emptyList()方法,这里记录一下。她的使用方式是:public void run() { ...... List list = buildList(param); ...... Object newNode = getNode(...); list.add(newNode); ......}public List buildList(Object param) { if (isIn原创 2021-03-25 01:25:27 · 4530 阅读 · 12 评论 -
《深入理解Java虚拟机》读书笔记(九)--早期(编译期)优化
目录一、Javac编译器1.1 解析与填充符号表1.1.1 词法、语法分析1.1.2 填充符号表1.2 注解处理器1.3 语义分析与字节码生成二、使用步骤1.引入库2.读入数据总结一、Javac编译器Javac的编译过程大致可以分为3个过程,分别是:解析与填充符号表过程插入式注解处理器的注解处理过程分析与字节码生成过程1.1 解析与填充符号表1.1.1 词法、语法分析 词法分析是将源码的字符流转变为标记(Token)集合,单个字符是程序编写过程的最小元素,而标记则是编译过程的最小元原创 2021-03-23 03:00:28 · 6110 阅读 · 2 评论 -
Thread.setDaemon设置守护线程
Java中线程分为用户线程(user thread)和守护线程(daemon thread),它们通过Thread的daemon属性标识:true表示守护线程,false表示用户线程。 一个Thread初始默认为用户线程(daemon默认为false),创建Thread的时候默认从当前线程"继承"daemon属性,见Thread.init方法:Thread parent = currentThread();this.daemon = parent.isDaemon(); 当虚拟机中剩余运行原创 2021-03-21 20:49:17 · 6048 阅读 · 5 评论 -
Tomcat源码分析--停止
前言在前文Tomcat源码分析--启动中,分析了Tomcat是如何启动并且阻塞主线程的:在我们提供port的前提下,会首先注册一个shutdownHook,然后根据port创建一个serverSocket阻塞等待socket连接,直到收到shutdown指令以达到守护运行的目的。脚本停止假设在server.xml中这样配置:<Server port="8005" shutdown="SHUTDOWN">...</Server>那么在Tomcat组件都启动后,原创 2021-03-21 16:24:08 · 4804 阅读 · 7 评论 -
Tomcat源码分析--启动
首先找到catalina.sh中的启动脚本:eval $_NOHUP "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \ -D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \ -classpath "\"$CLASSPATH\"" \ -Dcatalina.base="\"$CATALINA_原创 2021-03-21 15:44:01 · 12031 阅读 · 8 评论 -
JAVA之ShutdownHook源码分析
一、使用 Java中可以通过Runtime.getRuntime().addShutdownHook添加一个钩子,JVM在退出的时候会调用我们注册的钩子,借助它我们可以实现资源清理等操作。比如:public static void main(String[] args) { Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public v原创 2021-03-21 13:03:28 · 7579 阅读 · 4 评论 -
深入OpenJDK源码全面理解Java类加载器(下 -- Java源码篇)
目录前言一、双亲委派1.1 类加载器结构1.2 双亲委派二、使用步骤1.引入库2.读入数据总结前言 在深入openjdk源码全面理解Java类加载器(上 – JVM源码篇)我们分析了JVM是如何启动,并且初始化BootStrapClassLoader的,也提到了sun.misc.Launcher被加载后会创建ExtClassLoader和AppClassLoader。这篇文章主要从Java源码层面总结一下双亲委派、TCCL的应用等,然后在聊聊自定义类加载器的注意事项。一、双亲委派1.1 类加载器原创 2021-03-21 04:27:29 · 10366 阅读 · 16 评论 -
深入OpenJDK源码全面理解Java类加载器(上 -- JVM源码篇)
目录前言一、从JDK源码看双亲委派二、使用步骤1.引入库2.读入数据总结前言关于JVM类加载的基础理论知识,请参照《深入理解Java虚拟机》读书笔记(六)–虚拟机类加载机制(上)和《深入理解Java虚拟机》读书笔记(六)–虚拟机类加载机制(下)。一、从JDK源码看双亲委派注:博主是使用的是openjdk8,换了新电脑还没有去编译源码,所以看的是静态代码,关于如何编译和调试源码,网上不少文章都有介绍,这里就不赘述了我们都知道在Java类加载中,除了BootStrap加载器,App和Ext加载原创 2021-03-19 13:14:48 · 18212 阅读 · 29 评论 -
JSP是如何编译成servlet并提供服务的
概述服务端对外提供JSP请求服务的是JspServlet,继承自HttpServlet。核心服务入口在service方法,大体流程如下: 首先需要先获取请求的jspUri,如果客户端发起请求:http://xxx.xx.com/jsp/test.jsp,那么获取到的jspUri为:/jsp/test.jsp() 然后查看缓存(Map结构)中是否包含该jspUri的JspServletWrapper,如果没有就需要创建一个JspServletWrapper并且缓存起来,并调用JspServ原创 2021-03-17 00:23:27 · 11650 阅读 · 12 评论 -
《深入理解Java虚拟机》读书笔记(八)--类加载及执行子系统案例(Tomcat类加载、OSGI、动态代理)
一、Tomcat类加载器架构作为一个web服务器,需要解决以下几个问题:部署在同一个服务器上的web应用程序所使用的Java类库可以实现相互隔离。 部署在同一个服务器上的两个web应用程序所使用的Java类库可以互相共享。 服务器需要尽可能保证自身的安全不受部署的web应用程序影响。 JSP需要支持热更新由于存在上述问题,单独的一个classpath无法满足需求,所以web服务器都提供了好几个classpath路径供用户存放第三方类库。在Tomcat目录结构中,有3组目录(“/common/原创 2021-03-17 01:25:30 · 12790 阅读 · 19 评论 -
关于类加载的并发控制锁(ClassLoadingLock)
死锁在JDK1.7以前,java.lang.ClassLoader的一些核心方法是被synchronized修饰的,比如loadClass,以下摘自JDK6下java.lang.ClassLoader的部分方法:protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {...}private synchronized Class loa原创 2021-03-16 13:18:58 · 12361 阅读 · 15 评论 -
关于使用MethodHandle在子类中调用祖父类重写方法的探究
注:这个例子本出现在周志明先生的《深入理解Java虚拟机》虚拟机字节码执行引擎章节,介于有读者朋友有疑问,这里解释一下(原文在《深入理解Java虚拟机》读书笔记(七)--虚拟机字节码执行引擎(下))。这里直接把代码列出来:package test;import java.lang.invoke.MethodHandles;import java.lang.invoke.MethodType;import java.lang.reflect.Field;public class Te.原创 2021-03-11 13:25:50 · 5241 阅读 · 8 评论 -
《深入理解Java虚拟机》读书笔记(七)--虚拟机字节码执行引擎(下)
一、Java动态类型语言支持动态类型语言的关键特征是它的类型检查的主体过程是在运行期而不是编译期,满足这个特征的语言有很多,比如JavaScript、Python等,相对的,在编译期就进行类型检查的语言(如C++/Java等)就是最常用的静态类型语言。例如以下代码:obj.println("hello world");假设这行代码在Java语言中,并且变量obj的静态类型为java.io.PrintStream,那么变量obj的实际类型就必须是PrintStream的子类(实现了Print原创 2021-03-08 00:10:32 · 11094 阅读 · 29 评论