唯's Blog

笔者是一个热爱编程的 Java 程序员。

0%

ThreadLocalRandom 的坑

这是 JDK 提供的多线程环境下保证线程安全生成随机数的工具类

结论(使用注意点)

在线程自己的方法中调用 ThreadLocalRandom.current() 。(这样才能保证线程本身生成随机数的独立性)

禁止将 ThreadLocalRandom 提升为全局变量,例如:public static final ThreadLocalRandom random = ThreadLocalRandom.current() 这样会导致使用该变量的多线程生成随机策略一样的,核心底层随机策略的种子值 probe 不会再次初始化。

使用 ThreadLocalRandom,不能多个线程共享某线程返回的 ThreadLocalRandom 实例,需要每个线程分别调用ThreadLocalRandom.current().nextInt(1000),这样才能初始化每个线程的种子,才可以起到随机数的效果。

什么是隐式转换

当操作符(>,=…)与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容

转换规则

  • 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换
  • 两个参数都是字符串,会按照字符串来比较,不做类型转换
  • 两个参数都是整数,按照整数来比较,不做类型转换
  • 十六进制的值和非数字做比较时,会被当做二进制串
  • 有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp
  • 有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
  • 所有其他情况下,两个参数都会被转换为浮点数再进行比较

避免隐式转换

  • 应该避免隐式转换,隐式转换类型主要有字段类型不一致、in 参数包含多个类型,字符集类型不一致等。
  • 隐式转换可能导致索引失效,甚至会导致查询结果不准确
  • 如果字符串的第一个字符就是非数字的字符,那么转换为数字就是0
  • 如果字符串以数字开头
    • 如果字符串中都是数字,那么转换为数字就是整个字符串对应的数字
    • 如果字符串中存在非数字,那么转换为的数字就是开头的那些数字对应的值

配置重试机制

1
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;

刚性、线性一致性、强一致性

2PC

3PC

柔性、最终一致性

本地消息表

TCC(Try Confirm Cancel)

SAGA 事务

  • 更像一种设计模式,根据这种设计模式,可以完成最终一致性的分布式事务

框架

阿里开源的 Seta 框架就支持 TCC、SAGA

ConcurrentHashMap 的一些方法是线程安全的,一些不是。其中线程安全的包括:get、put、remove、containsKey。线程不安全的包括:size、clear、isEmpty。

其中 get 方法虽然是线程安全的,但是方法中并没有加锁。底层通过 volatile 保证读取的时的可见性(node.val)。Node 若是红黑树,则会加上读写锁进行查询。

上述方法所说的线程安全,可以保证单个方法调用的原子性 。如果存在多个方法调用的复合逻辑,可能就需要加锁保证线程安全了。

AOP 解决项目业务场景

  1. 操作日记记录
  2. 权限校验
  3. 敏感数据脱敏(API接口设计)

API 调用重试机制

使用 DelayQueue 实现重试机制。例如,接口调用失败或者请求超时后,可以将当前请求对象放入 DelayQueue,通过一个异步线程 take() 取出对象然后继续进行重试。如果还是请求失败,继续放回 DelayQueue。可以设置重试的最大次数以及采用指数退避算法设置对象的 deadline,如 2s、4s、8s、16s ……以此类推。DelayQueue的时间复杂度和Timer基本一致。

  1. 重试机制可能导致接口响应时长过长
  2. 重试接口要设计成幂等
  3. 超时重试、影响整个应用性能

Job 与 实时操作

在平常开发大家都会遇到批量处理的功能,具体实现有 Job 定时去扫描。

不实时操作的原因:

  1. 异步
  2. 下一个动作是另外一个系统的功能

job 频率执行太快,会出现空转浪费资源;job 频率执行太慢,可能会出现用户傻等,等待job执行。

因此单纯依赖定时任务,可以在前端添加一个 refresh 按钮,实时同步当前实体的数据。

JDK 9 新增 ifPresentOrElse() ,对我来说简洁了很大部分代码

JDK 9 新增 ifPresentOrElse() ,对我来说简洁了很大部分代码

查看所有线程,top默认显示所有进程第一行

top -H

查看所有存在的线程

ps xH

通过进程id查看线程数量

ps -mp

查看某个进程线程数

ps -T -p pid

查看特定某个进程线程使用内存情况

top -H -p pid

查看进程相关的线程信息

top -Hp pid –

磁盘相关

查看磁盘占用情况

df -h [目录]

查看文件夹大小

du -sh [目录]

Clean Code

整洁代码的原则

  • 代码大部分时候是用来维护的,而不是用来实现功能的
  • 优秀的代码大部分是自描述的,好于文档注释

说下比较适合写注释的两种场景:

  • public interface,向别人明确发布你功能的语义,输入输出,且不需要关注实现。
  • 功能容易有歧义的点,或者涉及比较深层专业知识的时候。比如,如果你写一个客户端,各种config参数的含义等。
  • 设计模式只是手段,代码清晰才是目的

当你的系统内大部分抽象只有一个实现的时候,要好好思考一下,是不是设计有点过度了,清晰永远是第一准则。

整洁代码的常见方法

  • code review
  • 勤于重构
  • 静态代码检查 (Sonar)
  • 多阅读开源代码和身边同学优秀的代码

整洁代码的常见技巧

通用技巧
  • 单一职责
  • 优先定义整体架构

在一个功能流程开发中,优先定义好类、以及方法的归属。方法可以不着急实现,这样开发的代码可读性很高

  • 清晰的命名
  • 避免过长的参数
  • 避免过长方法和类
  • 让相同长度的代码段表示相同粒度的逻辑
面向对象技巧
  • 贫血与领域驱动
  • 一个好的系统,一定离不开一套好的模型定义。梳理清楚系统中的核心模型,清楚的定义每个方法的类归属,无论对于代码的可读性、可交流性,还是和产品的沟通,都是有莫大好处的。
  • 为每个方法找到合适的类归属,数据和行为尽量在一起
  • 警惕static
  • static真正适用的场景:工具方法,而不是业务方法。
  • 巧用 method object
  • 面向接口编程
  • 正确使用继承和组合
代码复用技巧
  • 模板方法
  • extract method
  • 责任链
  • 为集合显式定义它的行为

整洁代码存在的问题

  • 整洁代码会影响性能,甚至导致程序慢15倍。我们需要在代码可维护性和高性能上进行权衡,说到权衡其实整个软件从架构层面的选择到整洁代码的优化,都是在权衡。通过本身丰富经验,技术的广度来选择合适的方案。没有最好,只有适合。

参考文章:https://tech.meituan.com/2017/01/19/clean-code.html