记一次OOM问题排查以及引出的GC优化

一)介绍

我们有一个服务,主要是给一些商家或者供应商查看售卖的票信息,例如报表下载和一些售票的查询等等。

因为平时用的人比较少,而且我们这边可以配置自动扩容的阈值,所以机器配置是 2C8G,两个机房分别 3 台。

项目很少发版本,最近一次的发版本还是前一个月。

二)问题出现

阅读更多

用FastThreadLocal替代ThreadLocal

在多线程编程的环境中,一个变量是可以被多个线程访问并修改的,如果想让一个线程,在不影响到其他线程的情况下,修改此变量,那么就需要将该变量改成自己私有的,这就是 ThreadLocal 的作用了。

ThreadLocal

Threadlocal 可以将一个变量作为自己的私有变量,可以在本线程内随意修改并且不影响到其他线程,如下 Demo:

image-20220724235813520

可以看到 t1 打印出 Thread1,t2 打印出 Thread2,两个线程的 ThreadLocal 都不受影响。

阅读更多

我就用了一个 assert,竟然让我损失了一杯星巴克

虽然有那么点标题党,但是确实让我损失了一杯星巴克,耽误了测试小姐姐的时间~~

简介

事情的起因是这样的,我们有一个接口是对外修改状态用的,例如状态有1,10,20,30,40,50 等等,状态的流转在业务上来说只允许操作一次,不然会导致一些重复的事件触发。

所以为了防止并发修改导致的系统问题,我们在修改数据库的时候,做了一个 CAS 判断

1
2
update tbl set status = ? where status = ? and order_id = ? 

阅读更多

使用lambda来优化责任链模式

责任链模式是设计模式的一种,可以为调用的对象进行一个链式处理,这种模式在 Java 的一些第三方库中经常见到。

而在业务开发中,这种需求也是很常见的,如果用好这个设计模式,对于代码的扩展性和可维护性都是非常有帮助的,例如常见的下单流程,就可以用责任链模式处理。

而在第三方库中,像 tomcat 的过滤器就是使用责任链模式进行处理

Tomcat 中的使用

在 tomcat 中,过滤器的实现就完全是责任链模式的使用了

阅读更多

CyclicBarrier详解

在上一篇的文章中有提到过 CountDownLatch ,其实 CyclicBarrier 也有异曲同工之妙,不过 CyclicBarrier 是等到所有的线程都到达一个点以后,然后再一起执行

有点像小时候一起去春游,必须等到所有的同学都到了学校,才能一起去坐车,不然就会一直等待。

构造函数

CyclicBarrier 的构造函数有两个,分别如下:

1
2
3
public CyclicBarrier(int parties) {
this(parties, null);
}
阅读更多

CountDownLatch详解

CountDownLatch

CountDownLatch 只有一个构造函数:CountDownLatch(int count)

其中 count 表示该信号量的数量,其中具体的实现类是 Sync,而 Sycn 又是继承自 AQS,实现了几个 AQS 的方法

在生产环境中可以用于分治思想,讲一些复杂的处理分成一些子任务,等所有处理任务处理完毕以后,主线程才会执行

还可以用于一些任务的流程检查,例如只有所有的检查都完毕以后,主线程才可以获取数据然后执行

阅读更多

Java中多线程使用经验

在子线程中获取父线程的ThreadLocal

如果想子线程想使用父线程的 ThreadLocal,那么父线程中的 inheritableThreadLocals 有值,这样子线程中的 init 方法就会自动的将父线程的 inheritableThreadLocals 设置为 ThreadLocal

但是因为子线程是在 init 方法中进行赋值的,所以如果子线程是由线程池创建的,那么该方法就又可能会失效,当线程池刚初始化完毕的时候,此时线程池中的还没有线程,当调用 execute 方法,此时就会 new 一个线程,那么这时候子线程是可以读取到父线程中 inheritableThreadLocals 的值

但是线程池中的线程是可以被复用的,所以后续如果线程不再创建的时候,那么子线程便不能再次获取父线程中的 inheritableThreadLocals,也就无法再将 ThreadLocal 进行父子线程的传递

如何感知到子线程的异常

阅读更多

Java中的SPI机制以及应用场景

SPI 全称是 Service Provider Interface,是 JDK1.5 新增的一个功能,允许不同的服务提供者去实现某个规定的接口,而且将具体的实现完全提供给使用方,允许使用方按需加载服务提供方的一些功能。

前言

提到 SPI,就不得不提下 API,以 dubbo 为例,服务提供方对外提供一系列 API,而使用方是不用关心服务提供方是如何实现具体的业务逻辑,只需要通过 RPC 调用远程服务即可。

API

这样的好处就是 client 端不用关心服务端的具体逻辑,方便服务的水平扩展以及解耦。

阅读更多

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

前言

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:代表线程的空闲时间,也就是线程等待多久以后可以被销毁
阅读更多