唯'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