TAG
Java
232 篇笔记
- 1.Exceptionhistorical
1. 异常体系 - Error:表示系统级的错误,程序处理不了也无法处理 - Exception:表示需要由程序处理的异常 - UnCheckedException:由名字可以看出不需要我们处理。是一种常见运行错误,只要程序设计得没有问题通常就不会发生 - CheckedException:由名字可
- 1.1 如何优雅的处理异常historical
1. 不优雅的处理方法 业务逻辑代码包裹在异常处理中,完全看不出实际的logic 2. 优雅的处理方法 2.1. 模板方法 - 模板类 - how to use 2.2. 接口 - 接口 //业务处理类作为一个接口 - how to use it
- 2.1 BloomFilterhistorical
1. 使用 2. 源码分析 - 关键属性 - 创建BloomFilter - put方法 MURMUR128 MITZ 32默认 抽象来看,put是写,mightContain是读,两个方法的代码有一点相似,都是先利用murmur3 hash对输入的funnel计算得到128位的字节数组,然后高低分
- 2.3 介绍historical
为什么有MyBatis ----------- - JDBC SQL写在Java代码中,硬编码严重 - Hibernate 完全的ORM,用面向对象的方式操作数据库。 但是不够灵活 - Mybatis 可以手写代码,SQL写在配置文件中 流程 -- 1. mybatis配置 SqlMapConfig
- 2.4 SpringCloudhistorical
1. 是什么 基于SpringBoot提供了一套微服务解决方案,包括服务网关、服务注册与发现、配置中心、全链路监控、负载均衡、熔断器等组件 2. 参考 - SpringCloud 面试题\ 江城宴的博客\-CSDN博客
- 2.7 介绍historical
组件 核心:DispatcherServlet 三大组件:HandlerMapping、HandlerAdapter、ViewResolver 用户编写:Handler、View 流程 1. 用户发送请求至前端控制器DispatcherServlet 2. DispatcherServlet收到请求
- 2.8 RateLimiterhistorical
1. 是什么 Google基于令牌桶算法实现的限流工具 2. 使用 3. 源码分析 3.1. 创建RateLimiter - RateLimiter - SmoothBursty - SmoothRateLimiter 3.2. 获取锁 - RateLimiter - SmoothRateLimit
- 2.9 使用方式historical
新建maven项目 pom.xml 配置mybatis - resources/jdbc.properties - resources/mybatis-config.xml 新建mapper - com.zsk.model.User - com.zsk.dao.UserMapper - mapper
- 2.10 Spring模块historical
1. Spring 核心容器 该层基本上是 Spring Framework 的核心。 - Spring Core - Spring Bean - SpEL (Spring Expression Language) - Spring Context 2. 数据访问/集成 该层提供与数据库交互的支持。
- 2.12 使用方式historical
新建项目 第一种方式 新建web项目 导入jar包依赖 在WEB-INF下新建lib目录,导入如下依赖。并且add as library 第二种方式 新建maven工程 添加web框架支持 maven pom 依赖 修改project配置lib包 配置spring mvc - WEB-INF/web
- 2.13 源码分析historical
1. 框架分层 2. 四大对象 MyBatis创建四大对象的时候,使用动态代理机制添加了拦截器. - Executor - ParameterHandler - ResultSetHandler - StatementHandler 3. 要分析的代码 4. 获取SqlSessionFactory对
- 2.14 源码分析historical
1. 打断点 我们在com.zsk.controller.TestController#test的方法上打上断点,查看调用栈如下 从中间一段调用栈看出主要经过 HttpServlet 、 FrameworkServlet 、 DispatcherServlet 这几个类的方法,我们从搞清楚这几个类的
- 2.16 线程安全historical
1. 线程安全问题 SpringMVC默认是单例的,所以如果有个实例属性并发修改的话会出问题。 1.1. 解决 - 使用ThreadLocal - 改用prototype多例 2. 参考链接 - Spring 是如何解决并发访问的线程安全性问题的\ 葛明的博客\-CSDN博客
- 2.19 Netty3.x源码分析historical
1. 总览 创建两个线程池,一个用于boss数组初始化,一个用于worker数组初始化 2. boss数组初始化的过程 所有的boss共享同一个线程池 1. 使用Selector.open打开选择器 2. 通过线程池执行一个run方法(这个方法是个死循环) 2.1 设置wakeup为false 2.
- 2.20 Apollohistorical
1. Apollo是什么 配置中心 2. 如何使用Apollo 3. Apollo原理 3.1. 架构 3.2. 推拉结合 - 修改配置后会存储到DB中 - AdminService会定时扫描DB中的配置,有变化则推送给客户端 - - 4. 参考 - ctripcorp/apollo: Apollo
- 2.21 Eurekahistorical
1. 是什么 如何设计注册中心.md - 采用了C-S架构的服务注册中心,提供服务注册与发现的功能 2. 使用 Getting Started \ Service Registration and Discovery 3. 原理 3.1. 服务注册与发现 3.1.1. 多级缓存机制 - 新的服务注册
- 2.22 Feignhistorical
1. Feign是什么 - 远程接口调用组件 - Feign=注解+Ribbon+RestTemplate 2. 为什么有Feign - Ribbon 使用HttpClient 或 RestTemplate 模拟http请求,步骤相当繁琐。 - 而Feign采用接口+注解的方式 。将需要调用的其他服
- 2.23 Gatewayhistorical
1. 是什么 Zuul 1.x版本的替代 2. 使用 2.1. 三大概念 - Route: 由ID、目标URI、一系列的断言和过滤器组成,如果断言为true则匹配 - Predicate: 路由的匹配条件 - Filter: 请求被路由前或者路由后进行处理 3. 原理 3.1. 异步非阻塞线程模型
- 2.24 Hystrixhistorical
1. Hystrix是什么 微服务的服务容错组件 2. 为什么需要Hystrix 本质上就是为什么需要熔断组件 如何设计容错组件.md 3. Hystrix使用 4. Hystrix功能 4.1. 服务熔断 1. 调用出现错误,开启一个时间窗(10s) 2. 在这个时间窗内,统计调用次数是否达到最小
- 2.25 Ribbonhistorical
1. Ribbon是什么 客户端+独立的负载均衡组件 2. 为什么需要Ribbon 换句话说就是为什么需要负载均衡。 所谓负载均衡就是把压力平均得分散到每个节点上,这样子一方面可以提高吞吐量,另一方面可以避免单节点压力过大宕机。 3. 使用 3.1. 关闭Ribbon懒加载 每个服务第一次请求的时候
- 2.26 Sleuthhistorical
1. 是什么 SpringCloud的链路监控,兼容Zipkin 2. 原理 2.1. 基于Zipkin - 库存服务调用商品服务,会把trace data丢给zipkin记录,dashboard上就能看到这条链路了 -
- 2.27 Zuulhistorical
1. 是什么 SpringCloud的网关组件 如何设计API网关.md 2. 使用 2.1. 灰度发布 开发了新功能,部署在少量机器上,在网关配置少量请求走这个新功能 2.2. 动态路由 所谓动态路由就是后端服务URI不是写死的,而是动态获取的 将后端服务URI存在配置中心或者数据库,定时加载,这
- 2.28 ApplicationListenerhistorical
1. ApplicationListener是什么 就是观察者模式。生产者发布事件,多个消费者监听这个事件处理各自的逻辑,如此解耦了生产者消费者。后续如果还有消费者需要处理这个事件再增加一个Listener即可 2. 如何使用事件监听机制 比如我们有个需要上报工单后需要写邮件、写日志,取消工单后也需
- 2.29 Bean作用域historical
1. 作用域分类 - singleton:每次从容器中获取的bean是同一个 - prototype:每次从容器中获取的bean,都会创建一个新的 - request:在Http请求中使用的同一个bean - session:在http session中使用的是同一个bean 2. 为什么有prot
- 2.32 Autowired原理historical
1. 类继承图 2. 原理分析 1. spring容器refresh的时候有两个关键步骤,registerBeanPostProcessors和finishBeanFactoryInitialization 2. registerBeanPostProcessors会向spring容器注入Autow
- 2.33 Spring事务源码分析historical
1. 开启事务管理注解 - @EnableTransactionManagement 这个注解向spring容器中导入了TransactionManagementConfigurationSelector,我们继续研究 2. TransactionManagementConfigurationSel
- 2.34 自定义Startershistorical
1. 两个问题 1. 这个场景需要使用的依赖是什么 2. 如何编写自动配置 - AutoConfiguration - Properites - 其他Service Bean 2. 实战 2.1. 创建empty project 2.2. 在project settings中添加两个maven pr
- 2.35 使用historical
1. 创建项目 2. pom.xml 3. 代码 - Application.java - TestApplication.java - TestController - TestService
- 2.36 Spring Boot Jar启动原理historical
1. 实验 - pom.xml - com.zsk.test.Application - 打jar包 - 解压后,使用tree命令查看结构 2. 可执行Jar的结构 - 包含 - Jar描述:META-INF/MANIFEST.MF - Spring Boot Loader:org/springfr
- 2.37 MyBatis自动配置historical
1. 开启MyBatis自动配置 spring boot自动配置mybatis是通过@MybatisAutoConfiguration注解开启的,如下 实现了InitializingBean,在spring实例化MybatisAutoConfiguration这个bean之后会调用其afterPro
- 2.38 原理分析historical
1. spring-boot-starter-web预先导入的自动配置类 - @WebMvcAutoconfiguration WebMvcAutoConfigurationAdapter是Spring Boot开启web mvc自动配置的类,如下 - 里面有一个静态内部配置类, WebMvcAut
- 2.40 原理分析historical
1. 开启ServletContainer自动配置 - @EmbeddedServletContainerAutoConfiguration EmbeddedServletContainerAutoConfiguration是spring boot自动配置嵌入式的servlet容器的自动配置类 他会
- 2.41 学习资源historical
参考 - 这可能是目前最透彻的Netty原理架构解析 \- 掘金 - 浅谈Netty的线程模型 \- 掘金 - 认真的 Netty 源码解析(二) \- 掘金 - 认真的 Netty 源码解析(一) \- 掘金 - Netty 源码阅读的思考\-\-\-\-\-\-耗时业务到底该如何处理 \- 莫那
- 2.43 线程隔离historical
1. 为什么需要线程隔离 防止有问题的调用耗尽整个服务的线程资源A 2. 隔离模式 2.1. 普通模式 默认是10个线程池,如下图 2.2. 舱壁模式 3. 线程池VS信号量 - 线程池隔离 - 使用的是独立的线程池 - 支持排队和超时 - 支持异步调用 - 线程调用会产生额外的开销 - 适用于 -
- 2.44 Bean生命周期historical
1. 流程 - 对Bean进行实例化 - 将值和引用注入到Bean对应的属性中 - 通过Aware接口把Spring底层组件注入Bean - BeanPostProcessor。执行额外的逻辑。在InitializingBean的前后执行,会把当前正在创建的Bean传入 - Initializing
- 2.45 是什么historical
1. AOP是什么 面向切面编程。不改变原有代码的基础上增加功能。 没有AOP之前,我们要增加一个打印日志的功能,可以直接修改原有代码,但这样业务代码和其他代码耦合在一起,不好维护; 有了AOP之后,我只需要在AOP中写这个打印日志的代码,原有的代码不用改动。 2. 实现原理 代理模式.md Spr
- 2.46 是什么historical
1. IOC解释 控制反转。 没有IOC之前我们使用一个对象首先要自己new出来; 有了Spring后是他帮我们new,然后放在容器里,我们直接从容器中获取对象使用。 2. 实现原理 工厂模式+反射+xml解析 扫描所有xml文件,使用dom4j解析,然后创建bean放入工厂中的map中,我们get
- 2.47 Spring常用注解historical
注解作用 @Configuration 相当于之前的applicationContext.xml,就是一个配置文件 @ComponentScan 配置扫描包,如果有@Controller,@Service,@Repositiry,@Component修饰的,则把它加入ioc容器中 @Bean 配合@
- 2.48 传播行为historical
1. 是什么 事务传播行为是Spring对数据库事务的增强特性,用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法时事务如何传播。 一般我们的事务都是写在service方法上,调用dao的方法时生效。 但是如果service A的方法调用service B的方法这种情况该怎么定义呢? 1.1.
- 2.49 原理分析historical
缓存管理和缓存类是怎么生成的 1. spring-boot-starter预先导入的自动配置类 - @CacheAutoConfiguration cache自动配置的类在于CacheAutoConfiguration,他导入了一个CacheConfigurationImportSelector 我
- 2.50 组件介绍historical
1. 是什么 一个异步事件驱动的网络框架,提供的api比Java原生的BIO、NIO的api好用的多,并且Netty能随意的切换NIO或者BIO的实现方式 2. 组件 2.1. Bootstrap 用于启动Netty。包含创建线程,创建socket等工作 2.2. EventLoop 是一个死循环,
- 2.51 堆外内存historical
1. 什么是堆外内存 就是内存不是分配在Java堆上的,而是在操作系统内存上的 2. NIO堆外内存 JDK NIO ByteBuffer.allocate方法分配的内存是在Java堆上面的,当数据需要传输的时候需要把堆上的这块内存数据原封不动的copy到操作系统内存中 而Java NIO Byte
- 2.52 BeanFactoryhistorical
1. BeanFactory和ApplicationContext BeanFacotry是实例化、配置、管理Bean的容器 ApplicationContext接口继承了BeanFactory接口,所以提供了BeanFactory的所有功能。他同时还继承了其他接口,如下图: 提供了额外的功能,如更
- 2.54 循环依赖historical
1. 循环依赖是什么 假设有两个Bean,Bean1和Bean2,如果Bean1依赖Bean2,Bean2依赖Bean1。 那么Spring创建Bean1的时候需要先创建Bean2,创建Bean2又需要先创建Bean1,这就叫做循环依赖 2. 循环依赖会导致什么问题 2.1. 构造器注入会抛出异常
- 2.55 Spring中如何让A和B两个bean按顺序加载historical
1. 使用@DependsOn注解 - TestController - TestService - 启动的时候输出 2. 参考 - Spring 中如何控制2个bean中的初始化顺序? \- 知乎 - @DependsOn或depends\-on配置的使用 \- 掘金
- 2.56 1.创建NioEventLoopGrouphistorical
1. NioEventLoopGroup类体系 如图,NioEventLoopGroup就是一个线程池,因此我们可以把任务封装成Runnable提交给NioEventLoopGroup执行 由继承的MultithreadXXX的名字可以看出,这玩意是一个多线程的池子,每个线程是什么呢--其实是Nio
- 2.57 2.Bootstrap的创建historical
1. 要分析的代码 2. 创建ServerBootstrap 创建ServerBootstrap没什么好说的,就是调用构造方法 3. 设置ServerBootstrap的属性 接下来就是设置他的属性,包括channel、handler、childHandler、option等 其中最重要channe
- 2.58 3.绑定端口historical
1. 要分析的代码 2. 服务端绑定端口 我们继续看绑定监听端口的逻辑 - bind 沿着bind往下追,最后到达doBind--关键方法 分为两步,一个是实例化、初始化并注册channel,另一个是真正绑定监听端口 2.1. 实例化、初始化并注册channel 1.实例化channel.md 2.
- 2.59 1.检测新连接historical
1. 打断点 有新连接过来的时候,会调用NioEventLoop中run方法的processSelectedKeys中OP ACCEPT逻辑 1.1. 启动客户端连接 我们在unsafe.read打个断点,启动服务器,并用telnet或者nc链接 2. 新连接进入 - 进入AbstractNioMe
- 2.60 pipeline的初始化historical
1. pipeline在什么时候被创建 不管是客户端还是服务端channel,都会在AbstractChannel的构造函数创建,每个channel都有一个自己的pipeline - newPipeline 2. pipeline是怎样的 - DefaultChannelPipeline pipel
- 2.61 1.服务端的创建historical
bind 从这行代码开始,追踪bind 最终会来到io.netty.bootstrap.AbstractBootstrap#doBind - initAndRegister channel 要知道channelFactory是哪个,我们需要回到这行代码 - io.netty.bootstrap.Ab
- 2.62 1.创建spring容器historical
1. 要分析的代码 分成创建ApplicationContext和从ApplicationContext中获取Calc的bean这两个过程,我们一步步跟踪 2. 创建ApplicationContext 2.1. AnnotationConfigApplicationContext构造方法 refr
- 2.63 启动原理historical
1. 启动SpringBoot 我们直接从SpringBoot启动的这行代码开始 忽略简单的嵌套调用,最终调用的代码如下: 首先会使用主配置类Application.class创建SpringApplication,然后传入启动参数args启动 2. 创建SpringApplication的过程 我
- 2.64 创建Executorhistorical
1. 要分析的代码 2. 创建构造线程的工厂 - newDefaultThreadFactory() 2.1. 设置线程池以及线程名 - DefaultThreadFactory - 设置了线程池的名字:nioEventLoopGroup - 每个线程的名字则是:nioEventLoopGroup-
- 2.65 2.创建NioSocketChannelhistorical
1. 要分析的代码 this是netty服务端的channel,ch是jdk nio客户端的channel,NioSocketChannel是netty客户端的channel 2. NioSocketChannel构造函数 2.1. 设置对OP READ感兴趣 - 父类AbstractNioByte
- 2.66 添加删除ChannelHandlerhistorical
1. 添加handler - DefaultChannelPipeline#addLast 1.1. 判断是否重复添加 - checkMultiplicity 1.2. 创建节点并添加至链表 - newContext - DefaultChannelHandlerContext - addLast0
- 2.67 2.服务端初始化historical
调用 bind - doBind - initAndRegister - channelFactory.newChannel() - init(channel) 我们重点看这个 ServerBootstrap - init
- 2.68 2.调用BeanFactoryPostProcessor的postProcess方法historical
1. 要研究的代码 - AbstractApplicationContext invokeBeanFactoryPostProcessors 关键的是这一句 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanF
- 2.69 自动配置原理historical
1. 开启spring boot自动配置 - @SpringBootApplicaiton 2. 向容器注入AutoConfigurationImportSelector - @EnableAutoConfiguration - EnableAutoConfigurationImportSelect
- 2.70 创建NioEventLoop数组historical
1. 要分析的代码 2. 传入NioEventLoop构造的参数 我们接着看 newChild(executor, args) ,跳转到 - NioEventLoopGroup#newChild 调用构造方法传入的参数 - this是NioEventLoopGroup自己 - executor是刚刚
- 2.71 3.分配线程及注册selectorhistorical
1. 要分析的代码 - io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read中有一段逻辑 2. 通过pipeline传播 pipeline.fireChannelRead(readBuf.get(i)) 会从pipe
- 2.72 3.注册selectorhistorical
调用 bind - doBind - initAndRegister - channelFactory.newChannel() - init(channel) - ChannelFuture regFuture = config().group().register(channel);我们重点看这
- 2.73 3.注册BeanPostProcessorhistorical
1. 要研究的代码 - AbstractApplicationContext registerBeanPostProcessors PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this); 这个作
- 2.74 4.向selector注册读事件historical
1. 要分析的代码 - io.netty.channel.AbstractChannel.AbstractUnsafe#register中的register操作 2. 打断点使用nc连接 其实跟服务器启动的时候向selector注册accept一样,我们看io.netty.channel.Abstr
- 2.75 4.服务端口绑定historical
调用 bind - doBind - initAndRegister - channelFactory.newChannel() - init(channel) - ChannelFuture regFuture = config().group().register(channel); - doB
- 2.76 4.实例化所有非懒加载的单实例beanhistorical
1. 要研究的代码 - finishBeanFactoryInitialization 这个步骤中尤其重要,他的preInstantiateSingletons会实例化所有非懒加载的单实例bean 2. 实例化所有非懒加载的单实例bean 2.1. 获取所有BeanName,一个个创建 - Defa
- 2.79 1.实例化channelhistorical
1. 要分析的代码 2. 反射调用Channel.class的构造方法创建实例 通过反射调用NioServerSocketChannel的构造方法 3. NioServerSocketChannel 3.1. 类体系 3.2. 构造方法 3.2.1. 创建JDK NIO底层的ServerSocket
- 2.80 2.真正绑定监听端口historical
1. 要分析的代码 这段逻辑将 channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE ON FAILURE); 做成一个Runnable,丢进channel关联的NioEventLoop去执行 1.1.
- 2.81 Inbound事件historical
1. 添加handler以备实验 我们添加三个InboundHandler进行试验 - InboundHandlerA - InboundHandlerB、C都一样 - NettyServer 2. 从pipeline开始调用 但我们使用ctx.pipeline().fireChannelRead(
- 2.82 ByteBufhistorical
数据结构 有两个指针,一个标记读的位置,一个标记写的位置。 0 < 读 < 写 < capacity 其中0到读之间的数据是无效的,读到写之间的数据是可读的,写到capacity之间是可写的 Api readXXX表示读数据,读指针往后移动 writeXXX表示写数据,写指针往后移动 set不移动指
- 2.83 1.给容器中注入AspectJAnnotationAutoProxyCreator组件historical
一般我们开启aop的功能是通过@EnableAspectJAutoProxy,所以首先查看其源码 1. 开启AOP - @EnableAspectJAutoProxy 关键在于导入的AspectJAutoProxyRegistrar这个类,他想容器中注入了一些组件 2. 注册AspectJAnnot
- 2.84 2.1AnnotationAwareAspectJAutoProxyCreator_Bean实例是如何创建的historical
1. 类继承图 如下图可看出AnnotationAwareAspectJAutoProxyCreator既是一个Bean后置处理器,又是一个实现了BeanFactoryAware接口的类 2. 分析调用栈 我们首先再注入的Calc和LogAspects以及上图关键方法打上断点,运行发现调用流程如下
- 2.85 Spring_AOP源码分析总结historical
创建日期 星期一 22 七月 2019 源码分析 ---- 1. @EnableAspectJAutoProxy @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAuto
- 2.86 2.初始化channelhistorical
1. 要分析的代码 2. 获取channel pipeline并加入一个ChannelInitializer 如上的代码最关键的在于获取当前channel对应的pipeline,并加入了一个ChannelInitializer。 而在这个ChannelInitializer中,把我们自己在 Serv
- 2.87 NioEventLoop的run方法historical
1. 要分析的代码 如上代码主要分为三块逻辑 - 死循环 - 检测是否有IO事件 select - 处理IO事件 processSelectedKeys - 处理异步任务队列 runAllTasks 处理IO事件和处理外部线程的异步任务两者的时间由ioRatio平衡,默认情况下50,代表的意思是一半
- 2.88 Outbound事件historical
1. 添加handler以便实验 - OutboundHandlerA、OutboundHandlerC - OutboundHandlerB - NettyServer 1.1. 使用nc连接 1.2. 输出结果 1.3. 解释 可以看出是从尾巴往头部调用我们的handler 2. 从pipeli
- 2.89 ByteBufAllocatorhistorical
类体系 分配内存的工具类 ByteBufAllocator 这里的api只能区分heap还是direct,另外两个维度由AbstractByteBufAllocator的buffer方法实现 AbstractByteBufAllocator buffer方法 我们看看他的buffer方法 分配直接内
- 2.90 2.2AnnotationAwareAspectJAutoProxyCreator是在什么时候起作用的historical
1. 继续研究BeanPostProcessor的postProcessBeforeInstantiation和postProcessAfterInitialization 我们把断点放行到 - AbstractAutoProxyCreator#postProcessBeforeInitializa
- 2.91 3.注册channelhistorical
1. 要分析的代码 2. 选择一个NioEventLoop - config()返回ServerBootstrap - Serverbootstrap的group()返回EventLoopGroup,EventLoopGroup继承了MultithreadEventLoopGroup,所以调用的是M
- 2.92 异常事件historical
1. 准备实验数据 - NettyServer - InboundHandlerA - InboundHandlerB - OutboundHandlerA - OutboundHandlerB 2. 开始debug 在 com.zsk.server.handler.InboundHandlerA#
- 2.94 2.3AnnotationAwareAspectJAutoProxyCreator具体是怎么创建代理对象的historical
1. 跟踪调用栈至postProcessBeforeInstantiation 我们继续放行断点,观察Calc实例的创建,断点主要停留在3个地方 2. 判断是否需要创建代理对象 - org.springframework.aop.framework.autoproxy.AbstractAutoPro
- 2.95 Channel类体系historical
1. Channel类体系 1.1. Channel 用于网络IO 1.2. AbstractChannel Channel的骨架实现 1.3. AbstractNioChannel 使用Selector实现的Channel 1.4. AbstractNioByteChannel 操作字节流、对RE
- 2.96 2.4目标方法是怎么执行的historical
1. 继续放行断点执行Calc.div 2. 进入到代理对象的intercept方法 - CglibAopProxy.DynamicAdvisedInterceptor#intercept 2.1. 获取拦截器链 - AdvisedSupport#getInterceptorsAndDynamicI
- 2.97 PooledByteBufAllocatorhistorical
newDirectBuffer 主要分为两步,第一步时获取PoolThreadCache,进而获取PoolArena,第二步是通过PoolArena分配内存 通过PoolThreadLocalCache获取PoolThreadCache PoolThreadLocalCache其实是一个FastTh
- 3.1 三大IO的对比historical
1. Java IO 和 系统调用的关系 1.1. 系统调用的IO类型 IO模型.md 2. Java IO分类 2.1. BIO 同步阻塞 IO:a connection to a thread。 在服务端需要使用多线程处理多个客户端连接和读写请求。一般ServerSocket一个线程(accep
- 3.2 BIO实现的多线程服务器historical
服务端编程,传统的情况是使用BIO。 但是使用这种会有两个阻塞点: ServerSocket.accept() 和 InputStream.read() ,因此单线程只能服务一个客户端。 为了服务多个用户,那么必须使用多线程来处理read请求,但是线程资源是有限的。 尤其对于长链接来说,保持如此之多
- 3.3 AIOhistorical
是什么 也是NIO包的类,如下 AsynchronousFileChannel: 用于文件异步读写; AsynchronousSocketChannel: TCP客户端异步socket; AsynchronousServerSocketChannel:TCP 服务端异步socket Asynchro
- 3.5 Bufferhistorical
基本用法 写入数据到Buffer 调用flip()方法 从Buffer中读取数据 调用clear()方法或者compact()方法 原理 有三个属性 - capacity - position - limit position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer
- 3.6 NIO实现的服务器historical
NIO vs BIO BIO NIO --- --- --- ServerSocket ServerSocketChannel Socket SocketChannel Selector SelectionKey 常见问题 客户端关闭的时候抛出异常,死循环 原因是没有判断 channel.read(
- 3.8 基础流historical
字节 InputStream - read 返回从InputStream流内读取到的一个字节内容。 当读完所有数据后返回-1.-1是一个int类型,不是byte或者char类型,这是不一样的。 - read(byte[]) 尝试读取与给定字节数组容量一样大的字节数,返回值说明了已经读取过的字节数 O
- 3.9 Channelhistorical
基本用法 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。 通道可以异步地读写。 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。
- 3.10 序列化协议historical
什么是序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境 自定义规则 使用ByteBuffer,遵循如下规则 例子
- 3.15 NIOhistorical
基本概念 - Channel和Buffer BIO基于字符流和字节流,NIO基于Channel和Buffer - Channel 类似于Socket - Buffer 类似于DataInputStream - NIO 从Channel<- Buffer的过程中,线程可以做其他的事情 - Select
- 3.16 数据包协议historical
1. 粘包拆包是什么 1. 实际数据 2. 粘包现象 3. 拆包现象 2. 出现的原因 TCP有问题是因为她是面向字节流的协议,无法分辨从哪个字节到哪个字节是一条完整的消息 UDP没问题是面向报文的协议,会保留报文之间的分界 首先“粘包”是不存在的,“粘包”这一个词主要是面向低水平或者没有受过比较系
- 4.2 switchhistorical
1. 支持的功能 对byte、short、int、char、String、enum支持条件分支 2. 源码分析 2.1. int 编译之后使用idea查看class文件发现没有变化,说明int是直接比较整数值 2.2. char - 反编译之后 比较的是char的ascii码 2.3. String
- 5.1 Cloneablehistorical
1. Cloneable接口是什么 一个标识性接口。实现了这个接口的对象可以调用 Object.clone 方法复制一份当前对象,这里复制的是 属性 2. 什么是对象克隆 将一个对象的属性拷贝到另一个有着相同类类型的对象中去 3. 如何实现对象克隆 3.1. 浅克隆 如果属性是基本类型,拷贝的就是基
- 5.2 Integerhistorical
1. 使用 2. 原理分析 2.1. 构造方法 Integer是不可变的,所谓的不可变是指: - 类使用final修饰 - 内部属性value使用final修饰 - 没有对外提供修改内部属性value的方法 2.2. valueOf方法 2.2.1. 范围正在-128-127的从缓存中取 - Int
- 5.3 ArrayListhistorical
1. 是什么 底层由数组实现的,可扩容的顺序表 有序、可以重复 2. 如何使用 3. 原理分析 3.1. uml 可以看出ArrayList是个List、可克隆、可序列化、可以使用下标访问 3.2. 构造方法 使用object数组,并且初始化长度为0 3.3. add方法 - 不扩容O(1),扩容O
- 5.4 Hashtablehistorical
1. 是什么 线程安全的hashmap 2. 如何使用 3. 原理分析 3.1. uml 可克隆,可序列化,实现了Map接口 3.2. 构造方法 使用链地址法(单链表)解决Hash冲突 初始化容量为11,默认的加载因子为0.75 3.3. put方法 3.3.1. 使用synchronized加锁
- 5.5 Objecthistorical
1. 方法 1.1. getClass 返回对象实例的class对象 1.2. hashCode 当需要使用hash table之类的数据结构时才会使用到这个方法,用来计算在数组中的位置。 当重写了equals方法的时候需要重写hashCode方法,必须保证 a.equals(b) 为true时,
- 5.6 PriorityQueuehistorical
1. PriorityQueue是什么 是一个队列,只不过加上了优先级的概念,换句话说队列里的元素是根据某种规则排好序的 2. 使用 - 输出 3. 源码分析 3.1. 属性 3.2. 有参构造 - initFromCollection 可以看出主要有两个步骤,一个是建立初始化元素到数组中,另一个是
- 5.7 Serializablehistorical
1. 序列化与反序列化 序列化:把内存中的对象以某个格式保存在磁盘中或者在网络上传输 反序列化:把磁盘中保存的或者从网络上传输过来的数据按照某个格式转换成内存中的对象 2. 如何使用 2.1. 实现Serializable接口 2.2. 使用ObjectInputStream、ObjectOutpu
- 5.8 HashSethistorical
1. 是什么 无序、不重复的集合,使用HashMap实现 2. 如何使用 3. 源码分析 3.1. uml 可序列化,可克隆 3.2. 构造方法 3.3. 属性 3.4. add方法 效率为O(1) 3.5. contains方法 效率为O(1) 3.6. remove方法 效率为O(1) 4. 总
- 5.9 Stringhistorical
1. 是什么 不可变、线程安全的字符串 2. 使用 3. 源码分析 3.1. 类的定义 String是不可变的 - 类使用final修饰 - 内部属性char value[]使用final修饰,说明引用不能改变 - 且内部没有对外提供修改内部属性char value[]的方法 3.2. 构造方法 3
- 5.10 LinkedListhistorical
1. 是什么 底层由双向链表实现的顺序表 有序、可以重复 2. 如何使用 3. 原理分析 3.1. uml 可以看出LinkedList是个List、双端队列、可序列化、可克隆 3.2. 构造方法 由头节点、尾节点、长度构成 3.2.1. 队列的节点Node 结构如下图: 3.3. add方法 -
- 5.11 LinkedHashMaphistorical
1. 是什么 - 使用双向链表+HashMap(数组+链表+红黑树)实现 - 相比于HashMap保存了顺序 - 迭代时输出的顺序是 - 按照插入节点的顺序来输出 - 也可以指定成按照访问的顺序输出(LRU) 2. 使用 - 按照插入节点的顺序来输出 - 按照访问的顺序输出 3. 实现 3.1. u
- 5.12 不可变对象historical
如何创建一个不可变对象 - 类使用final修饰 - 内部属性使用final修饰 - 如果是这个属性是引用对象 - 没有对外提供修改内部属性value的方法 - 如果对外返回的是copy过的对象
- 5.13 LinkedHashSethistorical
1. 是什么 2. 如何使用 3. 源码分析 3.1. 1.构造方法 3.2. 2.属性 3.3. 3.其他方法 同LinkedHashMap.md 4. 总结 底层使用LinkedHashMap实现,value使用newObject作为占位符
- 5.14 StringBufferhistorical
1. 是什么 线程安全的、可变字符串 其实就是在StringBuilder的基础上加了synchronized关键字 2. 如何使用 3. 原理分析 3.1. 构造函数 3.2. append方法 3.3. toString 3.4. subString
- 5.16 Map比较historical
1. HashMap1.7 vs HashMap1.8 HashMap1.7 HashMap1.8 ---------------------------- ---------- --------------- 数据结构 数组+链表 数组+链表+红黑树 冲突时链表中是头插法还是尾插法 头插 尾插 -
- 5.18 StringBuilderhistorical
1. 是什么 可变的、线程不安全的字符串 有点像ArrayList的实现,底层使用char数组,不够容量时需要扩容 2. 如何使用 3. 原理分析 3.1. 构造方法 3.2. append方法 直接在内部的char数组后面添加字符 如果容量不够需要扩容,为原来的2倍+2 - AbstractBui
- 5.19 List对比historical
1. ArrayList vs LinkedList ArrayList LinkedList ----------- ---------------------------------------------------- -------------------- 底层实现 数组 双向链表 复杂度
- 5.21 TreeSethistorical
1. 是什么 无序、不重复的集合,使用TreeMap实现 2. 使用 3. 源码分析 3.1. 构造方法 3.2. 属性 3.3. 其他方法 调用的TreeMap的方法,效率O(logN) 4. 总结 底层使用TreeMap实现,value使用newObject作为占位符
- 5.22 String比较historical
1. StringBuilder vs StringBuffer vs String String StringBuffer StringBuilder ----------- ------ ------------ ------------- 是否线程安全 √ √ × 是否可变 × √ √
- 5.23 SynchronizedListhistorical
1. 是什么 线程安全的list 2. 如何使用 3. 源码分析 3.1. synchronizedList方法 3.1.1. 调用SynchronizedList的构造方法 3.1.2. 初始化锁对象为当前正在构造的list 3.2. 其他方法 3.2.1. 使用synchronized代码块加锁
- 5.24 TreeMaphistorical
1. 是什么 基于红黑树(平衡二叉搜索树)实现,效率为O(logN)的key-value对。 迭代时输出的顺序是 - 按照key的自然顺序来遍历的 - 也可以使用自定义的Comparator进行排序 2. 使用 3. 源码分析 3.1. uml 3.2. 构造方法 3.3. put 3.4. get
- 5.25 Vectorhistorical
1. 是什么 线程安全的list 2. 如何使用 3. 源码分析 3.1. uml 可以看出是个List,可以克隆,可以序列化,可以使用下标访问 3.2. 构造方法 默认初始化长度为10,扩容时候的增量为两倍 3.3. add方法 不扩容的时候O(1),扩容O(N) 3.3.1. 加了synchro
- 5.26 fail-fasthistorical
1. fail-fast与fail-safe fail-fast:如果一个系统,当有异常或者错误发生时就立即中断执行,这种设计称之为fail-fast fail-safe:如果一个系统,可以在某种异常或者错误发生时继续执行,不会被中断,这种设计称之为fail-safe 2. Java迭代器的设计 f
- 5.27 JDK1.7historical
1. 问题 1.1. 手写HashMap 1.1.1. Entry 1.1.2. put操作 1.1.3. get操作 1.1.4. remove操作 1.2. 为什么构造函数中需要把capacity转成2的power 跟indexFor计算数组下标有关 不是计算Hash后对table.length
- 5.28 JDK1.8historical
1. 是什么 实现O(1)存取效率的key-value对数据结构 2. 如何使用 3. 原理分析 3.1. uml 可克隆,可序列化,实现了Map 3.2. 构造方法 3.3. put方法 总体伪算法如下: - 计算key的hash值 - 使用hash值&数组长度1计算改数据存放的位置i - tab
- 6.1 先谈硬件historical
要理解JMM,我们先要理解底层硬件的工作原理 1. 冯诺依曼体系结构 冯诺依曼提出将程序当作数据对待,将程序(指令)和数据用同样的方式储存。根据这个理论计算机被分成控制器、运算器、存储器、输出设备、输入设备这几个部件,如下图 其中运算器和控制器组合成了CPU,CPU执行指令或者操作数据的时候都要跟存
- 6.2 2.Synchronizedhistorical
1. 是什么 Java 中悲观锁的一种实现,相比于 volatile 是重量级锁,可以保证原子性、有序性、可见性 - 重量级 会引起上下文切换(会造成线程阻塞) - 原子性 synchronized 方法、synchronized 代码块被视作原子的 - 有序性 线程 A 对于锁 X 的释放发生于线
- 6.3 3.volatilehistorical
1. 是什么 Java 的轻量级锁,主要保证了有序性、可见性和一定的原子性 - 轻量级 相比于 synchronized,volatile 不会引起上下文切换(不会造成线程阻塞) - 原子性 对任意单个 volatile 变量的读/写具有原子性,但类似于 volatile++这种复合操作不具有原子性
- 6.4 4.CAShistorical
1. 是什么 要理解CAS,我们首先得了解乐观锁和悲观锁的概念。 1.1. 乐观锁与悲观锁 悲观锁:假设每次操作数据的时候总有人一起操作数据。因此我操作数据前先上锁,直到我操作完释放锁,别人都只能阻塞等待。 乐观锁:假设每次操作数据的时候没人跟我一起操作数据。因此我只在更新的时候检查一下有没有其他人
- 6.5 5.AQShistorical
1. 是什么 队列同步器,用于实现JUC包的其他并发工具类 2. 如何使用 一般我们不直接使用AQS,而是使用JUC中的其他工具类(如CountDownLatch等),这些工具类覆盖了几乎所有的使用场景,只有在这些工具类无法满足我们的需求时,才去用AQS实现自己的并发工具。 实现的一般的套路如下:
- 6.6 6.ReentrantLockhistorical
1. 是什么 在jdk5之前,synchronized效率极低,于是写了ReentranLock代替。 后来jdk7优化了synchronized,参考锁的优化.md。两者性能区别不大 1.1. synchronized vs ReentranLock 比较 Synchronized Reentra
- 6.7 7.Lock之Conditionhistorical
1. 是什么 类似object的wait和notify方法配合synchronized使用 condition的await和notify方法配合Lock使用,用来实现条件等待与唤醒 2. 如何使用 - 生产者消费者模式 3. 实现原理 3.1. uml 3.2. 创建Condition对象 - ne
- 6.8 8.CyclicBarrierhistorical
1. 是什么 可重复使用的计数器,让一堆线程互相等待,条件满足时一起往下执行 底层使用Lock+Condition实现阻塞等待和唤醒 2. 如何使用 2.1. 不带Runnable 当所有线程都到达await点的时候才一起往下执行 2.2. 带Runnale 当所有线程都到达await点的时候,最后
- 6.9 9.CountDownLatchhistorical
1. 是什么 不能重复使用的计数器。让一个线程等待其他线程完事再往下执行,类似于Thread.join() 底层使用AQS实现 2. 如何使用 - 注意 这里countdown的线程不会互相等待,谁先执行完谁就先退出 2.1. CountDownLatch VS CyclicBarrier Coun
- 6.10 10.CopyOnWriteArrayListhistorical
1. 是什么 这个list借鉴的是读写分离的思想(弱一致性) - 读的时候可以并发读,不加锁; - 写的时候需要加锁,复制一份原有数据进行修改,改完后写回list 2. 如何使用 3. 原理 3.1. 构造方法 - 可以看到底层是通过object数组实现, - 通过 getArray/setArra
- 6.11 11.CopyOnWriteArraySethistorical
1. 是什么 写时复制的set,有序不重复,底层使用CopyOnWriteArrayList实现 2. 如何使用 3. 原理分析 3.1. 构造方法 3.1.1. 底层使用CopyOnWriteArrayList实现 3.2. add方法 3.2.1. 转调CopyOnWriteArrayList
- 6.12 ArrayBlockingQueuehistorical
1. 是什么 使用Object数组实现的有界的阻塞队列 读读、读写、写写相互阻塞 2. 如何使用 2.1. 方法选择 方法\处理方式 抛出异常 返回特殊值 一直阻塞 超时退出 ------------ --------- --------- ------- ------------------ 插入
- 6.13 13.ThreadLocalhistorical
1. 是什么 不是线程同步机制,是一种线程数据隔离机制。 多线程共享变量通信的情况下,我们需要保证线程安全。一种方法是使用锁,另一种就是数据隔离机制。 ThreadLocal用的是后一种,即每个线程操作的是自己独有的数据,因此互相之间不会影响 2. 如何使用 3. 原理分析 3.1. uml Thr
- 6.14 14.ThreadPoolhistorical
1. 是什么 Java的线程池框架,他提供了“任务提交”与“任务执行”分离开的机制 1.1. 为什么需要线程池 用来复用线程 - 第一,线程的创建和销毁开销比较大 - 第二,线程数量过多的话会导致cpu忙于上下文切换而不“干活” 1.2. 使用场景 - 单个任务执行的时间不能太长 - 任务数很多 2
- 6.15 CompletableFuturehistorical
1. 是什么 用于异步编程。(准备说是非阻塞) Java中所谓的异步编程其实就是把阻塞的代码放在一个单独的线程中执行,并且有结果时会通知主线程 2. Future VS CompletableFutre Future CompletableFutre ------------------ -----
- 6.16 JDK1.7的ConcurrentHashMaphistorical
1. 构造方法 2. put方法 2.1. hash 2.2. ensureSegment 2.3. segment的put方法 2.3.1. scanAndLockForPut 2.3.2. rehash 3. get 4. containsKey方法 5. remove 5.1. segment
- 6.17 fork_joinhistorical
1. 是什么 并行执行的框架。把一个大任务分成多个小任务,每个小任务计算结果,最后汇总每个小任务的结果得到大任务的结果 1.1. 为什么出现 简单地使用线程池实现fork join需要考虑当前线程也跟着干活,而不是变成监工 2. 使用场景 计算密集型的任务 3. 如何使用 4. 原理分析 4.1.
- 6.18 Exchangerhistorical
1. 是什么 用于两个线程之间交换数据,数据的流向是双向的。即如果有Thread1和Thread2两个线程,Thread1传给Thread2一个数据 ,Thread2同时也会传给Thread1一个数据。 1.1. Exchanger对比SychronousQueue Exchanger Sychro
- 6.19 ReentrantReadWriteLockhistorical
1. ReentrantReadWriteLock是什么 ReentrantLock保证了同一时间只有一个线程可以在临界区读或者写数据,这意味着如果有两个读线程同时读取数据,ReentrantLock也只允许其中一个通过,但我们想要的是读可以并发执行,一旦有写则其他线程等待。如下表: 是否可以同时进
- 6.20 Semaphorehistorical
1. 是什么 限流工具类,同一时间只允许n个线程访问某资源 2. 原理分析 2.1. uml 可以看出Semaphore也有公平的和非公平之分,参考 - 非公平信号量.md - 公平信号量.md
- 6.21 Kernel_level_threadhistorical
1. 是什么 User level thread是操作系统感知不到的,由应用程序自己创建的线程并负责调度 Kernel level thread是操作系统能感知到的,并由操作系统负责调度 2. 如何验证Java线程是kernel级别的 Kernel level thread. Java程序通过JVM
- 6.22 Unsafehistorical
1. 是什么 Unsafe类顾名思义是个不安全的类,为什么说他不安全呢?我们得从C语言的内存管理说起。 1.1. C语言的内存管理 C语言中每个变量在内存中都有一个地址,我们可以用 &变量名 获取这个地址,然后用指针变量进行接收。说白了指针保存的就是内存地址。 我们要使用一段内存的时候需要用 mal
- 6.23 再谈JMMhistorical
了解了计算机底层知识后,再来看JMM就容易多了 1. 为什么需要JMM 之前提到过内存模型规定了程序的内存操作(读操作和写操作)所有可能的执行顺序中哪些是正确的,而不同的处理器架构有不同的内存模型,Java作为一个跨平台(OS和硬件)的语言,为了屏蔽底层的这些差异,定义了自己的内存模型:JMM 2.
- 6.24 锁的优化historical
1. JVM对锁的优化 JDK1.5之前sychronized内部锁的效率很低,1.5之后做了大量优化,提升了锁的性能。 优化措施主要有以下几个: 1.1. 锁的消除 JIT(不是javac)借助逃逸分析以及内联技术,分析某个同步代码块是否只能被一个线程访问, 是的话则不生成monitor相关的字节
- 6.25 手写AQShistorical
我们可以自己动手写一个简单的AQS,以更好地理解AQS实际的源码 1. 需求 1. 锁是排他的,一旦这个锁被某个线程占有,只要这个锁没被释放,他就不能被其他线程占有。因此需要保存当前占有锁的线程 2. 要有一个单独的字段表示当前锁的状态,是空闲还是已被占有 3. 同一时间有很多线程抢占锁,只有一个线
- 6.26 公平锁historical
所谓公平锁,遵循先到先得的原则。 即使锁已经被释放了,后到的也不能去抢占锁,得等到前面没人时才能去获取 1. 如何使用 2. 原理分析 2.1. 构造方法 2.1.1. 底层使用AQS实现 2.2. 加锁 - lock 2.2.1. 调用公平锁的lock方法 - FairSync.lock 2.2.
- 6.27 BlockingQueuehistorical
1. 是什么 线程安全的阻塞队列。 特点: - 先进先出: 既然是队列那肯定是先进先出 - 阻塞 支持在插入元素时,如果队列已满,那么阻塞,等待队列非满 也支持在删除元素时,如果队列为空,那么阻塞,等待队列非空 - 无界有界 数组容量的大小。无界其实是Integer.MAX VALUE - 线程安全
- 6.28 Executorshistorical
1. 使用 2. newCachedThreadPool 3. newScheduledThreadPool 4. newFixedThreadPool 5. newSingleThreadExecutor
- 6.29 JDK1.8的ConcurrentHashMaphistorical
1. 是什么 线程安全的HashMap,底层使用sychronized+CAS+HashMap的结构(数组+链表+红黑树)实现 2. 如何使用 3. 原理分析 3.1. 构造方法 3.1.1. Node 3.2. put方法【有加锁】 - putVal - 4行:计算key的hash,这里不是简单得
- 6.30 生产者消费者historical
1. 使用BlockingQueue 2. 使用wait notify 3. 使用Lock Condition 相比于上面的wait notify这里用了两个condition,这样子唤醒的时候就不会把生产者和消费者一起唤醒,只唤醒某一个即可(即生产者唤醒消费者,消费者唤醒生产者)
- 6.31 非公平ReadWriteLockhistorical
1. 是什么 无论队列前面是否有人排队等待锁,我直接去抢 2. 怎么使用 3. 源码分析 3.1. uml 3.2. 构造方法 - ReentrantReadWriteLock - ReentrantReadWriteLock.ReadLock - ReentrantReadWriteLock.Wr
- 6.32 非公平信号量historical
1. 是什么 限流,使用的非公平策略 2. 使用 3. 原理分析 3.1. 构造方法 3.1.1. 非公平Sync - NonfairSync - Sync 3.2. acquire 3.2.1. 调用AQS加共享锁 - AQS acquireSharedInterruptibly 3.2.1.1.
- 6.33 Thread.sleephistorical
1. sleep结束后什么时候唤醒 假设某个线程在 2020-01-09 22:47:54 运行上面这段代码,那么他会在 2020-01-09 22:47:55 立马往下执行么? - 答案 不一定,这段代码的意思只是在 2020-01-09 22:47:55 有机会去抢占CPU,而不是获得了CPU并
- 6.34 非公平锁historical
所谓公平锁,就是只要锁已经被释放了,那么不管是先到的还是后到的,都可以去抢占锁 1. 如何使用 2. 实现原理 2.1. 构造方法 2.2. 加锁 2.2.1. 使用非公平锁加锁 - NonfairSync lock方法 2.2.2. 通过AQS加锁 - AQS.acquire方法 由于Nonfai
- 6.35 LinkedBlockingQueuehistorical
1. 是什么 使用单向链表实现的有界的阻塞队列 读读、写写相互阻塞,读写不相互阻塞 吞吐量比ArrayBlockingQueue高 2. 如何使用 3. 源码分析 3.1. 构造方法 3.1.1. 底层使用单向链表+Lock+Condition实现 3.1.2. Node 结构如下图: 3.2. p
- 6.36 RejectedExecutionHandlerhistorical
1. RejectedExecutionHandler是什么 corePoolSize满了,blockingQueue也满了,maxPoolSize也满了,那么新的任务该怎么处理,这就看RejectedExecutionHandler 2. 分类 2.1. CallerRunsPolicy 处理策略
- 6.38 公平ReadWriteLockhistorical
1. 是什么 无论队列前面是否有人排队等待锁,我直接去抢 2. 怎么使用 3. 源码分析 3.1. uml 3.2. 构造方法 - ReentrantReadWriteLock - ReentrantReadWriteLock.ReadLock - ReentrantReadWriteLock.Wr
- 6.39 公平信号量historical
1. 是什么 限流,使用的公平策略 2. 使用 3. 原理分析 3.1. 构造方法 3.1.1. 公平Sync - FairSync - Sync 3.2. acquire 3.2.1. 调用AQS加共享锁 - AQS acquireSharedInterruptibly 3.2.1.1. 尝试加锁
- 6.40 线程状态historical
1. 线程状态枚举类 2. 状态流转 3. 参考 - Lifecycle and States of a Thread in Java \- GeeksforGeeks
- 6.41 PriorityBlockingQueuehistorical
1. 是什么 底层使用数组(二叉堆)实现的无界的阻塞队列 读读、读写、写写相互阻塞 可以排序 由于无界,所以put操作不会阻塞,但是take操作会阻塞(队列为空的时候) 1.1. 二叉堆 一颗完全二叉树,堆序性质为,每个节点的值都小于其左右子节点的值,二叉堆中最小的值就是根节点。 底层用数组进行存储
- 6.42 SynchronousQueuehistorical
1. 是什么 底层使用单向实现的阻塞队列,不存储元素 一个写者必须同时有一个读者才能进行下去,反之亦然。 否则写者将会一直阻塞或者读者将会一直阻塞 2. 使用 3. 原理 3.1. 构造方法 3.1.1. Transfer 3.1.2. QNode 3.2. put 阻塞 3.2.1. 调用Tran
- 6.43 Atomichistorical
1. 是什么 线程安全的原子类,底层使用CAS实现 2. 使用 以AtomicInteger为例 3. 原理分析 3.1. 构造方法 可以看到主要有三个属性: Unsafe unsafe 、 long valueOffset 和 volatile int value - 关于Unsafe类的解释参考
- 7.2 Java线上问题排查historical
1. 内存占用100% / 内存问题 1.1. 查找Java进程ID top 命令,按下 M 查看哪个java进程占用内存最高 1.2. 分析是否发生OOM OOM问题举例.md 2. CPU占用100% / 线程问题 2.1. 查找Java进程ID和线程ID 1. top 命令,按下 P 查看哪个
- 7.3 1.垃圾回收思想historical
1. 引用计数法 每个对象都有一个计数器,有变量引用时+1,引用失效则-1。当计数器为0的时候则对其进行回收 优点:简单高效 缺点:无法解决循环引用的问题 循环引用是指A对象引用B对象,B对象又引用A对象,但是A,B对象已不被任何其他对象引用 2. 可达性分析 从GC Root出发,通过引用关系遍历
- 7.4 类生命周期historical
1. 类生命周期 主要有加载、连接、初始化、使用、卸载这几个步骤 1.1. 加载 当我们的代码需要使用一个类的时候,这个类不在JVM中的时候就会加载 1.1.1. 由谁加载 由类加载器负责加载类加载器 1.1.1.1. 类加载器分类 - 启动类加载器负责加载JAVA HOME/lib - 扩展类加载
- 7.5 内存分区historical
1. JVM内存分区 1.1. 线程共享 1.1.1. 堆 - 在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。之所以说几乎所有,不是说所有的是因为较新版本的Java(从Java 6的某个更新开始)中,由于JIT编译器的发展和”逃逸分析”技术的逐渐成熟,
- 7.6 调优思路historical
1. 调优的目的 减少Full GC的次数/降低Full GC耗时/降低内存占用 所谓JVM优化,就是尽可能让对象都在新生代里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代进行垃圾回收,同时给系统充足的内存大小,避免新生代频繁的进行垃圾回收 2. 生产上整体步骤 2.1. 观察应用的G
- 7.7 2.垃圾回收算法historical
1. 标记清除 垃圾回收分成两个阶段 - 标记: 从GC Root出发,标记所有可达的对象。未被标记的就是垃圾对象 - 清除: 清除所有未被标记的对象 1.1. 特点 - 会产生内存碎片(不连续的内存空间), 需要分配较大的对象时,无法找到足够的连续内存空间。 2. 标记整理 分标记和整理阶段。 -
- 7.8 类初始化顺序historical
1. 什么时候会触发初始化 new一个实例或者main方法所在类 2. 初始化规则 如果父类没有初始化,那么首先初始化父类 3. 举例 3.1. 单个类的情况 - 结果 3.2. 继承的情况 - 结果 4. 参考 - Java类初始化顺序 \- code\-craft \- SegmentFault
- 7.9 对象分配规则historical
1. 规则 - 对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。 - 大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。 - 长期存活的对
- 7.10 3.垃圾回收器historical
1. JVM实际采用的垃圾回收算法:分代收集 根据对象的存活时间把内存分成新生代和老年代 - 新生代的特点是每次回收只有少量对象存活,因此采用改进的复制算法 改进的复制算法把内存空间分为Eden和Survivor区(8:1:1),只有10%的空间被浪费 - 老年代的特点是有大量对象存活,采用标记清除
- 7.11 自定义类加载器historical
1. 什么时候需要自定义类加载器 - 加载任意路径(比如非classpath)下的类文件 - 隔离加载类。这样不同应用的同名类都可以加载,比如tomcat 2. 如何自定义类加载器 1. 继承ClassLoader父类 2. 重写父类的方法 1. 如果走双亲委派机制那么重写 findClass 方法
- 7.12 逃逸分析historical
1. 逃逸分析是什么 就是分析一个对象的作用域。 逃逸有两种: - 方法逃逸:在本方法中定义的对象可以被外部方法访问 - 线程逃逸:在本线程中定义的对象可以被其他线程访问 2. 如何开启 逃逸分析的 JVM 参数如下: - 开启逃逸分析:-XX:+DoEscapeAnalysis - 关闭逃逸分析:
- 7.13 4.jvm参数选用具体的垃圾回收器historical
1. 新生代与老年代的匹配 2. JVM参数 -XX:+UseSerialGC 新生代使用Serial(复制算法),老年代使用Serial Old(标记整理) -XX:+UseParNewGC 新生代使用ParNew(复制算法),老年代使用Serial Old(标记整理) -XX:UseParall
- 7.14 对象的创建过程historical
1. 代码 2. 字节码 2.1. 解释 可以看出 Test test = new Test(); 分成三个步骤 1. 创建Test实例 2. 对Test实例的属性val赋值 3. 把Test实例的引用赋值给test变量 第一个步骤执行完对象处于半初始化状态, 问题在于第2、3步骤,这两个步骤没有很
- 7.15 垃圾回收historical
思想- 垃圾回收算法- 具体的垃圾回收器- jvm参数选用具体的垃圾回收器 1. 核心问题 GC是在什么时候,对什么东西,做了什么事情? 2. GC是在什么时候 当新生代空间(Eden+Fron)满的时候发生MinorGC,当老年代空间不足的时候发生MajorGC(一般也伴随着MinorGC),当S
- 7.16 对象在内存中的布局historical
1. 对象的组成 对象有两种,一个普通对象,另一种数组对象 1.1. 对象头(Object Header) 1. mark word 存储了对象的hashCode、GC信息、锁信息三部分 - 锁信息:不同的状态存储的位的意义不同。跟锁有关的是最后三个bit位,001表示无锁态,00表示轻量级锁,10
- 7.17 引用historical
1. 强软弱虚引用 1.1. 强 最普遍的引用。一个对象具有强引用,那么绝不会被回收。 - 例子 1.2. 软 内存足够的时候不会被回收,不足的时候会被回收。 使用SoftReference实现,一般用于缓存 - 例子 1.3. 弱 不管内存是否充足,一旦发生gc就会被回收 1.4. 虚 监控GC回
- 7.19 OOM问题举例historical
1. 为什么会发生OOM 得从GC回收过程说起(参考垃圾回收.md),可以看出GC之回收GCRoot达不到的对象的,GCRoot引用的对象是强引用,是没办法被回收的。 如果jvm内存中都是强引用对象,那么FullGC后jvm内存剩余空间很有可能不足以存放将要创建的新的对象,就会发生OOM 1.1.
- 7.20 性能分析工具historical
1. 自带工具 1.1. jps 用来查看进程信息 1.1.1. 查看本机java进程ID - 命令 - 结果 1.2. jinfo 用来查看设置的JVM信息 1.2.1. 打印运行中的程序的jvm参数 - 命令 - 结果 1.3. jstack 1.3.1. 打印Thread stack信息 -
- 7.21 JVM参数调优historical
1. 调整哪些参数 1. 针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值; 2. 年轻代和年老代将根据默认的比例(1:2)分配堆内存, 可以通过调整二者之间的比率NewRadio来调整二者
- 7.22 GC日志分析historical
1. Parallel GC日志 1.1. 新生代日志 1.2. 老年代日志 2. 工具 2.1. gchisto jewes/gchisto: A garbage collection log visualisation tool\. Originally hosted at https://ja
- 8.OOPhistorical
1. 创建子类对象时,父类对象会也被一起创建么 不会。 super关键词只不过是调用父类构造方法来初始化属性 2. 子类有没有继承父类的私有变量? 从继承的概念来说,private和final不被继承。Java官方文档上是这么说的。 从内存的角度来说,父类的一切都被继承(从父类构造方法被调用就知道了
- 9.Reflectionhistorical
1. 什么是反射 运行时 - 动态的获取类的信息:如属性、方法等 - 动态的创建对象、对属性赋值、调用对象的方法 2. 使用 - Java 反射由浅入深 \ 进阶必备 \- 掘金 3. 原理 - javac编译.java文件为.class文件 - 类加载器加载.class文件到方法区中 - 创建该类
- 10.Sockethistorical
1. 基础 Socket本质上就是IO,所以Socket的操作和IO基本类似。Socket的主题其实就是IO,NIO的主题 2. TCP 服务器通过ServerSocket监听在某个端口(NIO的ServerSocketChannel) 客户端通过Socket链接服务器(NIO的SocketChan
- 11.1 Servlethistorical
生命周期 - 调用init方法初始化 仅调用一次,在第一次创建servlet时调用 - 调用service方法处理客户端请求 每次客户端请求都会调用这个方法,转而调用doGet,doPost。。。 - 调用destroy方法终止 只会被调用一次 - 垃圾回收 参考链接 - Servlet 生命周期
- 11.2 调优historical
jvm调优 windows下为catalina.bat,在开头的位置加上jvm参数即可 观察参数是否设置成功 jps -l 获取PID jmap -heap PID查看结果 Coyote连接器调优 参数 说明 ------------- -------------------------------
- 11.3 使用historical
创建工程 新建servlet - com.zsk.controller.HelloServlet 配置web.xml 配置tomcat 运行访问 <http://localhost:8080/tomcat test war exploded/ 把idea生成的文件拷贝到tomcat下运行
- 11.4 Connectorhistorical
概述 -- - 连接器采用的是boss/worker的设计模式和adapter模式 - 有endpoint负责处接受tcp/ip请求--boss - 有processor负责将tcp/ip请求转成http请求--worker - Adapter负责将request/response转换成servle
- 11.5 启动流程historical
启动的主类 startup.bat 我们启动的时候通过startup.bat,他只是简单的调用catalina.bat catalina.bat 时序图 源码追踪 init Bootstrap init - main Catalina init - load StandardServer init
- 11.6 源代码层面historical
时序图 源码追踪 由启动流程.md tomcat启动由两个主要步骤,一个是init,另一个是start init主要的工作是绑定端口号(bind),start主要的工作是监听客户端链接(accept) 其中监听客户端链接的代码在这段逻辑 NioEndpoint - startInternal Acc
- 11.7 源码编译historical
1. 下载源码解压 Apache Tomcat® \- Apache Tomcat 8 Software Downloads 2. 改造成maven工程 在源码根目录添加pom.xml 3. 配置启动类 - org.apache.catalina.startup.Bootstrap 启动参数 4.
- 11.8 Containerhistorical
概述 - 一个server可以有多个service - 一个service由多个connector和一个container组成 - 一个container由一个engine - 一个engine有多个host【虚拟站点】 - 一个host由多个context【应用】 - 一个context由多个wr
- 11.9 配置文件层面historical
以 <http://localhost:8080/tomcat test/hello 为例 找到对应的connector 找到engine/service 找到对应的host:找到对应的context:tomcat test 找到对应的wrapper:hello
- 11.11 架构historical
是什么 tomcat是一个实现了servlet规范的web服务器软件。 他有两个作用,一个是http服务器,负责跟浏览器交互,处理请求和响应 另一个是servlet容器,将请求转发给实际的业务类处理并获取相应 为什么这样设计 为了解耦。如果只有http服务器,那么我们的业务都写在http服务器上,耦
- 12.泛型historical
1. 什么是泛型 指参数化类型,假设我们有个需求,写一个方法对数字进行排序,数字有Short、Integer、Long、Double等,他们的排序算法都是一样的,虽然可以写好几个重载的方法,但是好麻烦;换成Object作为参数的话运行时需要强制类型转换,如果传入一个String不就GG了 2. 泛型
- 13.1 Java8新特性historical
1. 新特性 - Lambda 表达式(允许把函数作为一个方法的参数) - Stream API - Date Time API - Optional 类 - PermGen空间被移除了,取而代之的是Metaspace 2. 常用 2.1. List- Map<String,List 3. 参考 -
- 14.Comparehistorical
1. Compare是什么 用于排序、分组的一个接口 2. 使用 2.1. 自定义Comparator 我们可以自定义Comparator并实现compare方法。 - compare方法有两个参数(o1,o2),表示相邻的两个元素; - 如果compare方法返回负数,表明o1应该排在o2前面;