类的初始化以及静态变量优化

前言

JVM 规范中写道:在加载类的时候,分为如下几个大类:

  • Creation and Loading
  • Linking
  • Initialization

每一步在 JVM 中都规定了具体的几个小节,但是今天本文的重点在于链接阶段,JVM 对一些静态变量做的一些优化,因此对于这里面的每一步具体是做什么的不展开讨论了。

类的初始化

阅读更多

线程池是如何关闭非核心线程的

在 Java 中,多线程的核心实现类是 ThreadPoolExecutor,该类提供了多线程的几个参数,用于开发人员自定义自己的线程池。

线程池的参数

1
2
3
4
5
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,
TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//...
}

线程池一共有 7 个参数,其中跟本次相关的有三个,分别是 corePoolSize、maximumPoolSize、keepAliveTime,这三个参数代表的意思如下:

  • corePoolSize:当线程池中的任务数小于 corePoolSize 或者线程池中的任务数大于 corePoolSize 但是小于阻塞队列的最大长度,那么线程池中的核心线程数就睡 <= corePoolSize
  • maximumPoolSize:当线程池中的任务数已经达到队列上限并且线程池中的线程数 < maximumPoolSize,此时线程池就会将线程数增加至 maximumPoolSize
  • keepAliveTime:代表线程的空闲时间,也就是线程等待多久以后可以被销毁
阅读更多

Redis中ziplist的实现二

添加 entry

ziplist 的添加过程大致如下:

  • 如果是一个新建的 ziplist,其中新增元素的时候,直接将 prevlen 设置为 0 即可

  • 如果是在中间位置新增,那么首先需要获取前一个 entry 的长度,因为后面的元素已经计算过了,因此直接拿来用即可

  • 如果是在尾部进行新增元素,那么需要计算其前一个元素的长度,因为 zlend 不会保存前一个最后一个尾节点的长度,因此需要在新增的时候计算

连锁更新

由于 ziplist 的设计,当前一个元素长度小于 254 的时候, prevlen 会以一个字节进行存储,但是当前一个元素大于 254 的时候,那么 prevlen 就会变成 5 个字节

阅读更多

Redis中ziplist的实现(一)

ziplist 是一个双向链表,但是不同于熟知的利用头指针和尾指针所形成的双向链表,ziplist 而是采用了一个特殊的实现

区别

普通的双向链表因为在每一个节点上都含有一个头节点和尾节点,所以在遍历的时候可以依次沿着每一个节点进行递归,而且在新增或者删除的时候,直接移动指针即可,并且每一个节点在内存中并不要求是连续的,只需要指针指向对应节点的内存地址即可。

但是 redis 使用的是内存,在内存满了的情况下就会造成 redis 的不可用,需要依据淘汰策略清理内存。

试想下如果 ziplist 像普通的双向链表一样,每一次新增一个 entry 都去申请一块新的内存,实现起来确实比较简单。

阅读更多

Redis中String的实现细节

String 是 Redis 中的基本数据结构之一,也是日常开发中使用最多的场景,例如秒杀扣库存,token缓存,详情缓存等,使用的频率还是比较高的,但是 Redis 中的 String 实现还是比较复杂的。

String 最底层的数据结构还是 char[],但是 Redis 在对数组进行封装的时候,做了一些细节上的优化

Redis 对象

在 Redis 中,每一个 Key 都可以称之为一个对象,Redis 包含了这个 Key 的类型,value 的内存地址,LRU 淘汰时间,引用计数等

1
2
3
4
5
6
7
struct RedisObject {
int4 type;
int 4 encoding;
int24 lru;
int32 refcount;
void *ptr;
}
阅读更多

浅谈 synchronized 和 volatile

一、锁

从细节上来说,锁分为 乐观锁、悲观锁

乐观锁适用于读多写少的场景,一般是通过 CAS 进行操作,因为不用加锁,所以性能上比悲观锁优秀太多

悲观锁适用于写多读少的场景,性能开销比较大。

1:乐观锁

阅读更多

LockSupport的一些琐事

LockSupport 是一个用于线程阻塞或者唤醒的类,位于 rt.jar,主要是通过 Unsafe 类来进行操作

该类的方法都是静态方法,最常使用的方法是 void park(Object blocker) 和 void unpark(Thread thread)

是AQS操作的基础类,阻塞线程的时候不需要加锁,比较方便

下面主要介绍这两种方法:

void park(Object blocker)

阅读更多

jackson中的范型

在 jackson 将字符串转为对象的时候,如果是不带有范型的数据类,那么在 strig 转 obj 的时候不会出现什么问题,如果你的对象带有范型的话,那么就需要注意了,稍不注意就会抛出如下异常

java.util.LinkedHashMap cannot be cast to XX

出现该原因的原因在于 jackson 转换对象的时候,如果没有识别到原始类型,会默认将其转为 LinkedHashMap,后续一旦使用该类,就会抛出上述错误
demo如下:

如果你是使用如下方法进行 string 转 object,那么范型会被映射称为 LinkedHashMap

1
public <T> T readValue(String content, Class<T> valueType)
阅读更多

Spring ContentNegotiation(内容协商)之原理篇(二)

简介

在了解这部分之前,你需要知道 Spring 都是通过 DispatcherServlet 来处理和分发请求的,如果不知道的话也不会影响到本文的阅读

在开启内容协商之后,URL 肯定是会变的,例如之前是 a/b,开启后则变成为 a/b.json 或者 a/b.xml

那么 Spring 首先第一步就需要解决如何将这个 url 映射到正确的 Controller 上的呢?

HandlerMapping

阅读更多

Spring ContentNegotiation(内容协商)之使用篇(一)

背景

随着业务系统的成熟,如果你的项目正好是公司的中台战略之一,但是下游系统的接收方式不统一,这一种情况在一些老的公司系统架构总经常出现,如果下游系统不方便兼容,那么就需要中台系统对外提供各种不同格式返回报文

内容协商

简单说就是服务提供方根据客户端所支持的格式来返回对应的报文,在 Spring 中,REST API 基本上都是以 json 格式进行返回,而如果需要一个接口即支持 json,又支持其他格式,开发和维护多套代码显然是不合理的,而 Spring 又恰好提供了该功能,那便是ContentNegotiation

在 Spring 中,决定一个数据是以 json、xml 还是 html 的方式返回有三种方式,分别如下:

阅读更多