190605-记录BigDecimal转int四舍五入的姿势

从db中查了一个BigDecimal数据,希望按照四舍五入的方式进行取整,发现直接使用 intValue 不太对,特此记录一下正确姿势

1
new BigDecimal(4.51).setScale(0, RoundingMode.HALF_EVEN).intValue()

190529-Java之HashMap迭代删除使用方法小结

map的迭代删除,和我们常见的list,set不太一样,不能直接获取Iteraotr对象,提供的删除方法也是单个的,根据key进行删除,如果我们有个需求,将map中满足某些条件的元素删除掉,要怎么做呢?

190521-JDK之List遍历删除的几种使用姿势

在实际的业务开发中,容器的遍历可以说是非常非常常见的场景了,遍历删除呢,用的机会也不会少,但你真的会用么

190515-老哥你真的知道ArrayList#sublist的正确用法么

我们有这么一个场景,给你一个列表,可以动态的新增,但是最终要求列表升序,要求长度小于20,可以怎么做?

这个还不简单,几行代码就可以了

1
2
3
4
5
6
7
8
public List<Integer> trimList(List<Integer> list, int add) {
list.add(add);
list.sort(null);
if (list.size() > 20) {
list = list.subList(0, 20);
}
return list;
}

190514-查看java进程jvm参数

java应用启动之后,有办法查看jvm参数么?

可以通过jps -v来实现

1
jsp -lv

如阿里的java进程输出如下

1
2
28996 com.aliyun.tianji.cloudmonitor.Application -Djava.compiler=none -XX:-UseGCOverheadLimit -XX:NewRatio=1 -XX:SurvivorRatio=8 -XX:+UseSerialGC -Djava.io.tmpdir=../../tmp -Xms16m -Xmx32m -Djava.library.path=../lib:../../lib -Dwrapper.key=drcJnFxDcXCZH8of -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.disable_console_input=TRUE -Dwrapper.pid=28989 -Dwrapper.version=3.5.27 -Dwrapper.native_library=wrapper -Dwrapper.arch=x86 -Dwrapper.service=TRUE -Dwrapper.cpu.timeout=10 -Dwrapper.jvmid=1
2358 sun.tools.jps.Jps -Denv.class.path=.:/usr/java/jdk1.8.0_131/lib/dt.jar:/usr/java/jdk1.8.0_131/lib/tools.jar:/usr/java/jdk1.8.0_131/jre/lib -Dapplication.home=/usr/java/jdk1.8.0_131 -Xms8m

190412-FastJson序列化对象中非字符串类型key输出非标准格式json串问题记录

采用fastjson作为项目的json序列化和反序列化工具,遇到一个蛋疼至极的问题, 如Map,key为int,则输出的字符串中,key没有被双引号括起来,导致前端解析失败

190403-HashMap的key典型错误使用姿势

记录一个非常低级的错误导致的java应用一直fullgc的问题;根本原因就是HashMap的key使用姿势不对

181230-使用Java Socket实现一个http服务器

作为一个java后端,提供http服务可以说是基本技能之一了,但是你真的了解http协议么?你知道知道如何手撸一个http服务器么?tomcat的底层是怎么支持http服务的呢?大名鼎鼎的Servlet又是什么东西呢,该怎么使用呢?

在初学java时,socket编程是逃不掉的一章;虽然在实际业务项目中,使用这个的可能性基本为0,本篇博文将主要介绍如何使用socket来实现一个简单的http服务器功能,提供常见的get/post请求支持,并再此过程中了解下http协议

180926-Java之数值型的字面值中使用下划线

之前偶然在一个开源项目中看到下面这种写法,深感惊奇,当时没有记录,后来果不其然就忘掉了这种写法,现在又看到这种写法,特此记录

1
long price = 1_000_123L;

180918-JDK之Deflater压缩与Inflater解压

JDK 压缩与解压工具类

在实际的应用场景中,特别是对外传输数据时,将原始数据压缩之后丢出去,可以说是非常常见的一个case了,平常倒是没有直接使用JDK原生的压缩工具类,使用Protosutff和Kryo的机会较多,正好在实际的工作场景中遇到了,现在简单的看下使用姿势

180911-获取应用中所有线程

如何获取应用中,所有活动的线程?

1
2
3
4
ThreadGroup group = Thread.currentThread().getThreadGroup();
// 激活的线程数加倍
int estimatedSize = group.activeCount() * 2;
Thread[] slackList = new Thread[estimatedSize];

上面是获取当前线程所在的ThreadGroup, 然后将这个分组内的所有线程丢到slackList数组中,实际测试时,数组大小可能是大于实际的线程数的(而且可能性特别大)

通过ThreadGroup,还可以获取上一层的Group, 然后遍历所有的线程

180907-IDEA编译Groovy脚本失败记录

在做 https://github.com/liuyueyi/quick-task 项目时,遇到了一个蛋疼的问题,不知道什么时候加了个Groovy脚本之后,整个项目就编译不过了,特此记录一下

180827-Java获取类路劲的几种姿势小结

I. Java获取类路劲的几种姿势小结

在Java环境中,如何获取当前类的路径,如何获取项目根路径,可以说是比较常见的需求场景了,下面简单的记录一下

180718-jar包执行传参使用小结

logo

jar包执行时传参的使用姿势

虽说我们现在大多不太直接使用jar包运行方式,目前比较主流的是将自己的服务丢在某个容器中(如tomcat,jetty等)运行,比如我之前所属的电商公司,就是将项目打包为war包,丢到tomcat容器中运行的

在使用SpringBoot时,可能会出现直接打包一个可执行的jar,然后运行,这种时候,通过java命令执行时,时可以传参的,那么问题来了,main方法可以如何优雅的解析这些传参呢?

180711-JVM定位分析CPU性能消耗

JVM分析CPU性能消耗

分三步走,看下JVM中的线程占用的CPU资源,以及定位这些线程为什么如此消耗资源

180706-BigDecimal除法的精度问题

BigDecimal除法的精度问题

在使用BigDecimal的除法时,遇到一个鬼畜的问题,本以为的精度计算,结果使用返回0,当然最终发现还是自己的使用姿势不对导致的,因此记录一下,避免后面重蹈覆辙

180704-JDK常用监控参数

相关博文: jvm调优的工具介绍

小结一下用的几个调优参数,特别是自带的jvisualvm,比较好用,但是功能丰富完整方面比jprofile要欠缺一点,后面有时间补上jprofile的使用过程

180615-精度计算BigDecimal

180615-精度计算BigDecimal

目前接触的业务中,对数据的精度要求比较高,因此不再使用基本的float,double,改为用BigDecimal进行存储和相关的计算,端午前的这一篇博文,则简单的介绍下BigDecimal的使用姿势,早点回家早点放假

180607-手写定长数组

手写定长数组

有个背景场景如下:

一天划分为1440分钟,每分钟记录一个数据块,然后用一个数据结构存储着1440个数据块,随着时间的推移,每过一分钟,向这个数据结构中添加一块,并移除最前的那个;其次就是我希望根据当前的时间,可以获取往前n分钟的数据块

简单来说,上面的需求解析如下:

  • 一个数组,容量为1440
  • 频繁的新增和删除
  • 随机的访问

后面两个就限制了ArrayList和LinkedList的使用场景了,所以为了满足这个场景,然后写了一个简单的数据结构

180606-Linux下jdk中文乱码问题解决

linux下jdk中文乱码问题解决

之前遇到过一次中文乱码问题,是通过在jdk的jre目录下的lib/fonts文件中添加simsun.ttf字体文件解决,但是这次遇到一个奇怪的问题,同样的字体拷贝过去后,中文不乱但是英文乱码了

记录一下解决过程:

  • 主要思路就是给系统安装中文字体,让系统本身就支持中文即可

180604-之时间戳的取整小TIP

时间戳的取整小TIP

一个简单的背景,持有ms为单位的时间戳,需要判断两个时间戳是否为同一分钟

180530-通过反射获取泛型类的实际参数

反射获取泛型类的实际参数

泛型用得还是比较多的,那么如何获取泛型类上实际的参数类型呢?

ConcurrentHashMap之1.7与1.8小结

I. ConcurrentHashMap 两种实现方式小结

1. 锁分段机制

HashMap的底层数据结构是数组+hash链表的方式,非线程安全

ConcurrentHashMap 采用锁分段机制,底层数据结构为二维数组,其中第一层是Segment的数组,每个Segment持有一把独立的锁,而Segment的结构和HashMap很相似;这就是锁分段机制;线程安全

关注几个点:

  • ConcurrentHashMap 如何定位 Segment, 如何定位 HashEntry
  • 修改的加锁逻辑,如何进行扩容
  • 读数据时,如何做到不加锁但保证线程安全的?

1. 定位逻辑

同样是利用hash值进行定位,这里分为两步定位,首先是确定Segment,其次是Segent中的HashEntry

  • hash值都是通过再hash后得到的(避免hash碰撞)
  • 通过再hash值,取高位,然后与Segment数组的长度求余,获取Segment的位置
  • 在Segment中,通过再hash值与数组的长度求余,定位HashEntry在数组中的索引,然后遍历hash链表定位具体的HashEntry

注意其中Segment是hash值取高位进行定位的,后者直接hash值进行求余定位的,这样做的目的就是为了避免两次哈希后的值一样,导致元素虽然在Segment里散列开了,但是却没有在HashEntry里散列开

2. 添加数据

  • 添加数据的逻辑,首先依然是通过上面的定位获取Segment
  • 对Segment加锁,防止其他线程同步修改
  • 第一步判断是否需要对Segment里的HashEntry数组进行扩容
  • 第二步定位添加元素的位置然后放在HashEntry数组里。

是否需要扩容

  • 在插入元素前会先判断Segment里的HashEntry数组是否超过容量(threshold),如果超过阀值,数组进行扩容。
  • Segment的扩容判断比HashMap更恰当,因为HashMap是在插入元素后判断元素是否已经到达容量的,如果到达了就进行扩容,但是很有可能扩容之后没有新元素插入,这时HashMap就进行了一次无效的扩容

如何扩容

  • 扩容的时候首先会创建一个两倍于原容量的数组,然后将原数组里的元素进行再hash后插入到新的数组里
  • 为了高效ConcurrentHashMap不会对整个容器进行扩容,而只对某个segment进行扩容

3. 线程安全的不加锁读

查询数据时,没有加锁,这又是如何保证线程安全的呢?

  • 如果在查询过程中,没有线程对容器进行修改,则没有问题
  • 如果有线程同步修改呢?

有以下几个机制来保障

  • 每个节点HashEntry除了value不是final的,其它值都是final的,这意味着不能从hash链的中间或尾部添加或删除节点,因为这需要修改next引用值,所有的节点的修改只能从头部开始。
  • 对于put操作,可以一律添加到Hash链的头部
  • 但是对于remove操作,可能需要从中间删除一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。为了确保读操作能够看到最新的值,将value设置成volatile,这避免了加锁

针对增加和删除具体分析:

对于PUT操作,如果在读取时,已经定位到对应的HashEntry索引,根据这个hash链表进行从头到尾的遍历,如果在遍历前已经插入,因为volatile,所以遍历的第一个就是目标所在;而如果已经在链表查询中间,再插入,可以认为是本次查询之后才新加入的数据,查不到也是ok的

对于删除而言,因为删除链表中间的HashEntry时,会新生成一个链表,将原来的节点拷贝过来;那么读取的遍历就有两种可能,落到新链表上,没问题;落到老链表上,仍旧读取旧数据,也认为是OK的

4. 计算size

先采用不加锁的方式,连续计算元素的个数,最多计算3次:

1、如果前后两次计算结果相同,则说明计算出来的元素个数是准确的;
2、如果前后两次计算结果都不同,则给每个Segment进行加锁,再计算一次元素的个数


2. Node + CAS + Synchronized

jdk1.8重写了ConcurrentHashMap的实现,丢掉了锁分段的二维数组结构,改用Node数组进行

从结构上来看,1.8中ConcurrentHashMap的数据结构和HashMap的一样,区别只是在于修改时如何保障线程安全

1. 新增一个数据

新插入一个HashEntry的内容时,首先是定位到具体的Node,如果这个位置没有加过数据,直接通过cas插入即可(无锁)

如果存在node,则锁住这个node(因此其他修改如果需要方位这个node对应的链表时,会竞争锁);然后将数据插入到链表尾部(或者红黑树的指定位置)

2. 修改一个已经存在的数据

定位node,锁住,然后修改对应的value值即可

3. 删除数据

定位node,如果不存在表示不用删;存在时,锁住这个node,然后遍历查找到需要删除的节点,干掉

4. 读数据

不加锁,和hashmap的原理差不多;需要注意的是Node节点中的value和next都是volatile的,即线程对这些数据的修改对其他线程是立马可见的

II. 其他

个人博客: 一灰灰Blog

基于hexo + github pages搭建的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见识有限,如发现bug或者有更好的建议,随时欢迎批评指正

扫描关注

QrCode

基于ForkJoin构建一个简单易用的并发组件

基于ForkJoin构建一个简单易用的并发组件

在实际的业务开发中,需要用到并发编程的知识,实际使用线程池来异步执行任务的场景并不是特别多,而且一般真的遇到了需要并发使用的时候,可能更加常见的就是直接实现Runnable/Callable接口,丢到Thread中执行了;或者更高级一点,定义一个线程池,扔进去执行;本片博文,将从另一个角度,借助JDK提供的ForkJoin,来设计一个简单易用的并发框架

Java学习之NIO相关

Java NIO学习小结

前面一篇主要学习了下IO的流式操作,接下来就是重头戏了,NIO,又称为New IO

当然也是得抱着问题来学习这个东西了,希望可以通过本文,可以学习到:

  • 什么是NIO
  • NIO相比较与IO有什么特点
  • 同步,非同步,阻塞,非阻塞是什么鬼
  • 几种IO模型

Java学习之IO相关

Java IO学习小结

IO操作算是java的一个基本知识点了,比如我们常见的网络IO,文件读写等,而且这一块基本上大家并不会频繁的来操作,大多会用一些封装得好用的工具来代替,某些时候真的需要做的时候,基本上也很难一下子很顺利的写完

本篇将主要集中在:

  • 几种IO分类
  • 字节IO和字符IO的转换
  • 装饰类IO是什么
  • 序列化的实现机制

JVM学习之垃圾回收机制

jvm的垃圾回收算法,除了我们熟悉的引用计数判断对象是否活着之外,其他还有那些有意思的东西呢?

总是听到的年轻代年老代又是啥?

传说中的YoungGC(MinorGC) 和 FullGC的时机是什么,又干了些啥?

JVM学习之内存结构

JVM学习之内存结构

java运行时对象创建在什么地方?堆和栈空间又有什么区别?听闻已久的Young,Old区又是什么鬼?听说有个常量池,这个又是啥

要想在脑海中清晰的布局一个java类在加载到使用的过程中,整个类生命周期中,各项数据究竟最终落在哪个板块上,就需要了解下JVM的内存区域了

JVM学习之Java类的加载机制

JVM学习之Java类的加载机制

平常我们使用java的多,深入到jvm层的机会却很少,平时若不关注,也不会清楚java文件编译后的class文件是如何被jvm加载到内存,如何进行初始化,如何进行运行的

因此这里主要学习的目标就是class文件的加载,会包含以下内容:

  • 什么是类加载
  • 类加载的过程
  • 什么时候触发类加载
  • 类加载器
  • 双亲委托机制

Java并发学习之线程池ThreadPoolExecutor的小结

Java并发学习之线程池ThreadPoolExecutor的小结

本篇博文将带着问题来回顾小结多线程池相关的知识点

  1. 线程池的几种创建方式
  2. 线程池的优点是什么
  3. 应用场景
  4. 如何使用
  5. 实现原理
  6. 异常状况怎么处理
  7. 线程池中任务的提交执行后,到线程执行,执行完成的整个流程逻辑
  8. 线程池中的线程回收机制

Java可以如何实现文件变动的监听

Java可以如何实现文件变动的监听

应用中使用logback作为日志输出组件的话,大部分会去配置 logback.xml 这个文件,而且生产环境下,直接去修改logback.xml文件中的日志级别,不用重启应用就可以生效

那么,这个功能是怎么实现的呢?

Java中变量的初始化顺序

Java中变量的初始化顺序

在写一个通用的报警模块时,遇到一个有意思的问题,在调用静态方法时,发现静态方法内部对静态变量引用时,居然抛出了npe,仿佛是因为这个静态变量的初始化在静态方法被调用时,还没有触发,从而导致这个问题,因此今天专门来学习下静态成员的初始化顺序,以及上面这个问题导致的原因

JavaWeb三大组件之Filter学习详解

JavaWeb三大组件之Filter学习详解

Filter基本上可以说存在所有的JavaWeb项目中,比如最基本的一个请求参数的编码CharacterEncodingFilter,大家一般都会配置下,那么filter是干嘛的呢?

本篇将主要集中在fitler的以下几个知识点:

  • 干嘛的
  • 怎么用
  • 多个Filter执行的先后顺序
  • 注意事项

JavaWeb三大组件之Servlet学习

JavaWeb三大组件之Servlet学习

平时直接用springmvc较多,都没怎么接触底层的Servlet,导致对一些基本的知识点了解都不够,所以今天专门的抽出时间来学习一下

带着问题出发,看下可以怎么玩

  • 如何自定义一个Servlet
  • 自定义的Serlvet如何工作
  • servlet的优先顺序怎么判定
  • servlet匹配是怎样的 (url-mapping…)
  • 如何获取参数(get请求参数,post请求参数,上传文件)
  • 如何返回数据(返回页面,返回文件,返回二进制)
  • 请求头和返回头的设置

Android学习之旅1D:首屏页的开发

Android学习之旅:第一天

采用依葫芦画瓢的方式来学习android的开发,准备逐步的开发出《一封》这个app

本片主要记录了SplashActivity的开发过程

兼容ImageIO读取jpeg图片变红

兼容ImageIO读取jpeg图片变红

使用ImageIO.read()方法,加载图片为BufferedImage对象时,对于某些图片,会出现变红的case

Batik渲染png图片异常的bug修复

Batik渲染png图片异常的bug修复

batik是apache的一个开源项目,可以实现svg的渲染,后端借助它可以比较简单的实现图片渲染,当然和java一贯处理图片不太方便一样,使用起来也有不少坑

下面记录一个bug的修复过程

jvm调优的工具介绍

jvm调优实战笔记之基础知识简介

I. 背景

java后端,提供了一个svg渲染的服务,在qps较大时,会出现频繁的gc,而此时的服务器性能本身并没有达到瓶颈(cpu,load,io都不太高)因此考虑调整一下jvm的相关参数,看是否可以提升服务性能

JDK学习之反射的使用姿势一览

反射的学习使用

日常的学习工作中,可能用到反射的地方不太多,但看看一些优秀框架的源码,会发现基本上都离不开反射的使用;因此本篇博文将专注下如何使用反射

本片博文布局如下:

  1. 反射是什么,有什么用,可以做什么
  2. 如何使用反射
  3. 实例:

    • 利用反射方式,获取一个类的所有成员变量的name及值
    • 通过反射方式,修改对象的私有成员变量
    • 会通过写一个BeanUtils实现对象的成员变量值拷贝来覆盖上面两个场景

Java学习之深拷贝浅拷贝及对象拷贝的两种方式

I. Java之Clone

0. 背景

对象拷贝,是一个非常基础的内容了,为什么会单独的把这个领出来讲解,主要是先前遇到了一个非常有意思的场景

有一个任务,需要解析类xml标记语言,然后生成document对象,之后将会有一系列针对document对象的操作

通过实际的测试,发现生成Document对象是比较耗时的一个操作,再加上这个任务场景中,需要解析的xml文档是固定的几个,那么一个可以优化的思路就是能不能缓存住创建后的Document对象,在实际使用的时候clone一份出来

ForkJoin 学习使用笔记

ForkJoin 学习使用笔记

Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×