聊聊 dubbo 的线程池
之前没注意到这一块,只是比较模糊的印象 dubbo 自己基于 ThreadPoolExecutor 定义了几个线程池,但是没具体看过,主要是觉得就是为了避免使用 jdk 自带的那几个(java.util.concurrent.Executors),防止出现那些问题
看下代码目录主要是这几个
- FixedThreadPool:创建一个复用固定个数线程的线程池。
简单看下代码public Executor getExecutor(URL url) { +聊聊 dubbo 的线程池 | Nicksxs's Blog 0%聊聊 dubbo 的线程池
之前没注意到这一块,只是比较模糊的印象 dubbo 自己基于 ThreadPoolExecutor 定义了几个线程池,但是没具体看过,主要是觉得就是为了避免使用 jdk 自带的那几个(java.util.concurrent.Executors),防止出现那些问题
看下代码目录主要是这几个![]()
- FixedThreadPool:创建一个复用固定个数线程的线程池。
简单看下代码public Executor getExecutor(URL url) { String name = url.getParameter("threadname", "Dubbo"); int threads = url.getParameter("threads", 200); int queues = url.getParameter("queues", 0); diff --git a/leancloud.memo b/leancloud.memo index 9e44d69594..3f0eba9c3a 100644 --- a/leancloud.memo +++ b/leancloud.memo @@ -153,4 +153,5 @@ {"title":"Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析","url":"/2022/03/07/Leetcode-349-两个数组的交集-Intersection-of-Two-Arrays-Easy-题解分析/"}, {"title":"Leetcode 83 删除排序链表中的重复元素 ( Remove Duplicates from Sorted List *Easy* ) 题解分析","url":"/2022/03/13/Leetcode-83-删除排序链表中的重复元素-Remove-Duplicates-from-Sorted-List-Easy-题解分析/"}, {"title":"给小电驴上牌","url":"/2022/03/20/给小电驴上牌/"}, +{"title":"Leetcode 4 寻找两个正序数组的中位数 ( Median of Two Sorted Arrays *Hard* ) 题解分析","url":"/2022/03/27/Leetcode-4-寻找两个正序数组的中位数-Median-of-Two-Sorted-Arrays-Hard-题解分析/"}, ] \ No newline at end of file diff --git a/leancloud_counter_security_urls.json b/leancloud_counter_security_urls.json index 46fc80b9f1..eed780d77f 100644 --- a/leancloud_counter_security_urls.json +++ b/leancloud_counter_security_urls.json @@ -1 +1 @@ -[{"title":"村上春树《1Q84》读后感","url":"/2019/12/18/1Q84读后感/"},{"title":"2020 年终总结","url":"/2021/03/31/2020-年终总结/"},{"title":"2020年中总结","url":"/2020/07/11/2020年中总结/"},{"title":"2019年终总结","url":"/2020/02/01/2019年终总结/"},{"title":"34_Search_for_a_Range","url":"/2016/08/14/34-Search-for-a-Range/"},{"title":"2021 年中总结","url":"/2021/07/18/2021-年中总结/"},{"title":"AQS篇二 之 Condition 浅析笔记","url":"/2021/02/21/AQS-之-Condition-浅析笔记/"},{"title":"2021 年终总结","url":"/2022/01/22/2021-年终总结/"},{"title":"AQS篇一","url":"/2021/02/14/AQS篇一/"},{"title":"AbstractQueuedSynchronizer","url":"/2019/09/23/AbstractQueuedSynchronizer/"},{"title":"add-two-number","url":"/2015/04/14/Add-Two-Number/"},{"title":"Apollo 的 value 注解是怎么自动更新的","url":"/2020/11/01/Apollo-的-value-注解是怎么自动更新的/"},{"title":"Clone Graph Part I","url":"/2014/12/30/Clone-Graph-Part-I/"},{"title":"Comparator使用小记","url":"/2020/04/05/Comparator使用小记/"},{"title":"Filter, Interceptor, Aop, 啥, 啥, 啥? 这些都是啥?","url":"/2020/08/22/Filter-Intercepter-Aop-啥-啥-啥-这些都是啥/"},{"title":"G1收集器概述","url":"/2020/02/09/G1收集器概述/"},{"title":"Disruptor 系列二","url":"/2022/02/27/Disruptor-系列二/"},{"title":"Disruptor 系列一","url":"/2022/02/13/Disruptor-系列一/"},{"title":"Leetcode 021 合并两个有序链表 ( Merge Two Sorted Lists ) 题解分析","url":"/2021/10/07/Leetcode-021-合并两个有序链表-Merge-Two-Sorted-Lists-题解分析/"},{"title":"JVM源码分析之G1垃圾收集器分析一","url":"/2019/12/07/JVM-G1-Part-1/"},{"title":"Leetcode 028 实现 strStr() ( Implement strStr() ) 题解分析","url":"/2021/10/31/Leetcode-028-实现-strStr-Implement-strStr-题解分析/"},{"title":"Leetcode 053 最大子序和 ( Maximum Subarray ) 题解分析","url":"/2021/11/28/Leetcode-053-最大子序和-Maximum-Subarray-题解分析/"},{"title":"Leetcode 104 二叉树的最大深度(Maximum Depth of Binary Tree) 题解分析","url":"/2020/10/25/Leetcode-104-二叉树的最大深度-Maximum-Depth-of-Binary-Tree-题解分析/"},{"title":"Leetcode 105 从前序与中序遍历序列构造二叉树(Construct Binary Tree from Preorder and Inorder Traversal) 题解分析","url":"/2020/12/13/Leetcode-105-从前序与中序遍历序列构造二叉树-Construct-Binary-Tree-from-Preorder-and-Inorder-Traversal-题解分析/"},{"title":"Leetcode 121 买卖股票的最佳时机(Best Time to Buy and Sell Stock) 题解分析","url":"/2021/03/14/Leetcode-121-买卖股票的最佳时机-Best-Time-to-Buy-and-Sell-Stock-题解分析/"},{"title":"Leetcode 124 二叉树中的最大路径和(Binary Tree Maximum Path Sum) 题解分析","url":"/2021/01/24/Leetcode-124-二叉树中的最大路径和-Binary-Tree-Maximum-Path-Sum-题解分析/"},{"title":"Leetcode 155 最小栈(Min Stack) 题解分析","url":"/2020/12/06/Leetcode-155-最小栈-Min-Stack-题解分析/"},{"title":"Leetcode 160 相交链表(intersection-of-two-linked-lists) 题解分析","url":"/2021/01/10/Leetcode-160-相交链表-intersection-of-two-linked-lists-题解分析/"},{"title":"Leetcode 2 Add Two Numbers 题解分析","url":"/2020/10/11/Leetcode-2-Add-Two-Numbers-题解分析/"},{"title":"Leetcode 234 回文链表(Palindrome Linked List) 题解分析","url":"/2020/11/15/Leetcode-234-回文联表-Palindrome-Linked-List-题解分析/"},{"title":"Leetcode 236 二叉树的最近公共祖先(Lowest Common Ancestor of a Binary Tree) 题解分析","url":"/2021/05/23/Leetcode-236-二叉树的最近公共祖先-Lowest-Common-Ancestor-of-a-Binary-Tree-题解分析/"},{"title":"Leetcode 3 Longest Substring Without Repeating Characters 题解分析","url":"/2020/09/20/Leetcode-3-Longest-Substring-Without-Repeating-Characters-题解分析/"},{"title":"Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析","url":"/2022/03/07/Leetcode-349-两个数组的交集-Intersection-of-Two-Arrays-Easy-题解分析/"},{"title":"Leetcode 4 寻找两个正序数组的中位数 ( Median of Two Sorted Arrays *Hard* ) 题解分析","url":"/2022/03/27/Leetcode-4-寻找两个正序数组的中位数-Median-of-Two-Sorted-Arrays-Hard-题解分析/"},{"title":"Leetcode 42 接雨水 (Trapping Rain Water) 题解分析","url":"/2021/07/04/Leetcode-42-接雨水-Trapping-Rain-Water-题解分析/"},{"title":"Leetcode 48 旋转图像(Rotate Image) 题解分析","url":"/2021/05/01/Leetcode-48-旋转图像-Rotate-Image-题解分析/"},{"title":"Leetcode 83 删除排序链表中的重复元素 ( Remove Duplicates from Sorted List *Easy* ) 题解分析","url":"/2022/03/13/Leetcode-83-删除排序链表中的重复元素-Remove-Duplicates-from-Sorted-List-Easy-题解分析/"},{"title":"leetcode no.3","url":"/2015/04/15/Leetcode-No-3/"},{"title":"Linux 下 grep 命令的一点小技巧","url":"/2020/08/06/Linux-下-grep-命令的一点小技巧/"},{"title":"MFC 模态对话框","url":"/2014/12/24/MFC 模态对话框/"},{"title":"Maven实用小技巧","url":"/2020/02/16/Maven实用小技巧/"},{"title":"Number of 1 Bits","url":"/2015/03/11/Number-Of-1-Bits/"},{"title":"Path Sum","url":"/2015/01/04/Path-Sum/"},{"title":"Redis_分布式锁","url":"/2019/12/10/Redis-Part-1/"},{"title":"Reverse Bits","url":"/2015/03/11/Reverse-Bits/"},{"title":"Reverse Integer","url":"/2015/03/13/Reverse-Integer/"},{"title":"two sum","url":"/2015/01/14/Two-Sum/"},{"title":"ambari-summary","url":"/2017/05/09/ambari-summary/"},{"title":"binary-watch","url":"/2016/09/29/binary-watch/"},{"title":"docker-mysql-cluster","url":"/2016/08/14/docker-mysql-cluster/"},{"title":"docker比一般多一点的初学者介绍","url":"/2020/03/08/docker比一般多一点的初学者介绍/"},{"title":"docker比一般多一点的初学者介绍三","url":"/2020/03/21/docker比一般多一点的初学者介绍三/"},{"title":"docker比一般多一点的初学者介绍二","url":"/2020/03/15/docker比一般多一点的初学者介绍二/"},{"title":"docker使用中发现的echo命令的一个小技巧及其他","url":"/2020/03/29/echo命令的一个小技巧/"},{"title":"gogs使用webhook部署react单页应用","url":"/2020/02/22/gogs使用webhook部署react单页应用/"},{"title":"invert-binary-tree","url":"/2015/06/22/invert-binary-tree/"},{"title":"minimum-size-subarray-sum-209","url":"/2016/10/11/minimum-size-subarray-sum-209/"},{"title":"C++ 指针使用中的一个小问题","url":"/2014/12/23/my-new-post/"},{"title":"mybatis 的 $ 和 # 是有啥区别","url":"/2020/09/06/mybatis-的-和-是有啥区别/"},{"title":"mybatis 的缓存是怎么回事","url":"/2020/10/03/mybatis-的缓存是怎么回事/"},{"title":"openresty","url":"/2019/06/18/openresty/"},{"title":"pcre-intro-and-a-simple-package","url":"/2015/01/16/pcre-intro-and-a-simple-package/"},{"title":"php-abstract-class-and-interface","url":"/2016/11/10/php-abstract-class-and-interface/"},{"title":"rabbitmq-tips","url":"/2017/04/25/rabbitmq-tips/"},{"title":"redis 的 rdb 和 COW 介绍","url":"/2021/08/15/redis-的-rdb-和-COW-介绍/"},{"title":"redis数据结构介绍-第一部分 SDS,链表,字典","url":"/2019/12/26/redis数据结构介绍/"},{"title":"redis数据结构介绍三-第三部分 整数集合","url":"/2020/01/10/redis数据结构介绍三/"},{"title":"redis数据结构介绍二-第二部分 跳表","url":"/2020/01/04/redis数据结构介绍二/"},{"title":"redis数据结构介绍五-第五部分 对象","url":"/2020/01/20/redis数据结构介绍五/"},{"title":"redis数据结构介绍六 快表","url":"/2020/01/22/redis数据结构介绍六/"},{"title":"redis数据结构介绍四-第四部分 压缩表","url":"/2020/01/19/redis数据结构介绍四/"},{"title":"redis淘汰策略复习","url":"/2021/08/01/redis淘汰策略复习/"},{"title":"redis系列介绍七-过期策略","url":"/2020/04/12/redis系列介绍七/"},{"title":"redis系列介绍八-淘汰策略","url":"/2020/04/18/redis系列介绍八/"},{"title":"redis过期策略复习","url":"/2021/07/25/redis过期策略复习/"},{"title":"rust学习笔记-所有权三之切片","url":"/2021/05/16/rust学习笔记-所有权三之切片/"},{"title":"rust学习笔记-所有权二","url":"/2021/04/18/rust学习笔记-所有权二/"},{"title":"rust学习笔记-所有权一","url":"/2021/04/18/rust学习笔记/"},{"title":"spark-little-tips","url":"/2017/03/28/spark-little-tips/"},{"title":"spring event 介绍","url":"/2022/01/30/spring-event-介绍/"},{"title":"summary-ranges-228","url":"/2016/10/12/summary-ranges-228/"},{"title":"swoole-websocket-test","url":"/2016/07/13/swoole-websocket-test/"},{"title":"wordpress 忘记密码的一种解决方法","url":"/2021/12/05/wordpress-忘记密码的一种解决方法/"},{"title":"《垃圾回收算法手册读书》笔记之整理算法","url":"/2021/03/07/《垃圾回收算法手册读书》笔记之整理算法/"},{"title":"介绍一下 RocketMQ","url":"/2020/06/21/介绍一下-RocketMQ/"},{"title":"介绍下最近比较实用的端口转发","url":"/2021/11/14/介绍下最近比较实用的端口转发/"},{"title":"从丁仲礼被美国制裁聊点啥","url":"/2020/12/20/从丁仲礼被美国制裁聊点啥/"},{"title":"从清华美院学姐聊聊我们身边的恶人","url":"/2020/11/29/从清华美院学姐聊聊我们身边的恶人/"},{"title":"关于公共交通再吐个槽","url":"/2021/03/21/关于公共交通再吐个槽/"},{"title":"关于读书打卡与分享","url":"/2021/02/07/关于读书打卡与分享/"},{"title":"周末我在老丈人家打了天小工","url":"/2020/08/16/周末我在老丈人家打了天小工/"},{"title":"在老丈人家的小工记三","url":"/2020/09/13/在老丈人家的小工记三/"},{"title":"在老丈人家的小工记五","url":"/2020/10/18/在老丈人家的小工记五/"},{"title":"在老丈人家的小工记四","url":"/2020/09/26/在老丈人家的小工记四/"},{"title":"寄生虫观后感","url":"/2020/03/01/寄生虫观后感/"},{"title":"我是如何走上跑步这条不归路的","url":"/2020/07/26/我是如何走上跑步这条不归路的/"},{"title":"分享记录一下一个 git 操作方法","url":"/2022/02/06/分享记录一下一个-git-操作方法/"},{"title":"上次的其他 外行聊国足","url":"/2022/03/06/上次的其他-外行聊国足/"},{"title":"看完了扫黑风暴,聊聊感想","url":"/2021/10/24/看完了扫黑风暴-聊聊感想/"},{"title":"聊一下 RocketMQ 的 DefaultMQPushConsumer 源码","url":"/2020/06/26/聊一下-RocketMQ-的-Consumer/"},{"title":"聊一下 RocketMQ 的 NameServer 源码","url":"/2020/07/05/聊一下-RocketMQ-的-NameServer-源码/"},{"title":"聊一下 RocketMQ 的消息存储之 MMAP","url":"/2021/09/04/聊一下-RocketMQ-的消息存储/"},{"title":"聊一下 RocketMQ 的消息存储三","url":"/2021/10/03/聊一下-RocketMQ-的消息存储三/"},{"title":"分享记录一下一个 scp 操作方法","url":"/2022/02/06/分享记录一下一个-scp-操作方法/"},{"title":"聊一下 RocketMQ 的消息存储二","url":"/2021/09/12/聊一下-RocketMQ-的消息存储二/"},{"title":"聊一下 RocketMQ 的消息存储四","url":"/2021/10/17/聊一下-RocketMQ-的消息存储四/"},{"title":"聊一下 RocketMQ 的顺序消息","url":"/2021/08/29/聊一下-RocketMQ-的顺序消息/"},{"title":"聊一下 SpringBoot 中使用的 cglib 作为动态代理中的一个注意点","url":"/2021/09/19/聊一下-SpringBoot-中使用的-cglib-作为动态代理中的一个注意点/"},{"title":"聊一下 SpringBoot 中动态切换数据源的方法","url":"/2021/09/26/聊一下-SpringBoot-中动态切换数据源的方法/"},{"title":"聊在东京奥运会闭幕式这天-二","url":"/2021/08/19/聊在东京奥运会闭幕式这天-二/"},{"title":"聊在东京奥运会闭幕式这天","url":"/2021/08/08/聊在东京奥运会闭幕式这天/"},{"title":"聊聊 Dubbo 的 SPI 续之自适应拓展","url":"/2020/06/06/聊聊-Dubbo-的-SPI-续之自适应拓展/"},{"title":"聊聊 Dubbo 的 SPI","url":"/2020/05/31/聊聊-Dubbo-的-SPI/"},{"title":"聊聊 Dubbo 的容错机制","url":"/2020/11/22/聊聊-Dubbo-的容错机制/"},{"title":"搬运两个 StackOverflow 上的 Mysql 编码相关的问题解答","url":"/2022/01/16/搬运两个-StackOverflow-上的-Mysql-编码相关的问题解答/"},{"title":"给小电驴上牌","url":"/2022/03/20/给小电驴上牌/"},{"title":"聊聊 Java 中绕不开的 Synchronized 关键字-二","url":"/2021/06/27/聊聊-Java-中绕不开的-Synchronized-关键字-二/"},{"title":"聊聊 Java 的 equals 和 hashCode 方法","url":"/2021/01/03/聊聊-Java-的-equals-和-hashCode-方法/"},{"title":"聊聊 Java 中绕不开的 Synchronized 关键字","url":"/2021/06/20/聊聊-Java-中绕不开的-Synchronized-关键字/"},{"title":"聊聊 Java 的类加载机制一","url":"/2020/11/08/聊聊-Java-的类加载机制/"},{"title":"聊聊 Java 的类加载机制二","url":"/2021/06/13/聊聊-Java-的类加载机制二/"},{"title":"聊聊 Java 自带的那些*逆天*工具","url":"/2020/08/02/聊聊-Java-自带的那些逆天工具/"},{"title":"聊聊 Linux 下的 top 命令","url":"/2021/03/28/聊聊-Linux-下的-top-命令/"},{"title":"聊聊 RocketMQ 的 Broker 源码","url":"/2020/07/19/聊聊-RocketMQ-的-Broker-源码/"},{"title":"聊聊 Sharding-Jdbc 的简单使用","url":"/2021/12/12/聊聊-Sharding-Jdbc-的简单使用/"},{"title":"聊聊 Sharding-Jdbc 的简单原理初篇","url":"/2021/12/26/聊聊-Sharding-Jdbc-的简单原理初篇/"},{"title":"聊聊 dubbo 的线程池","url":"/2021/04/04/聊聊-dubbo-的线程池/"},{"title":"聊聊 mysql 的 MVCC 续篇","url":"/2020/05/02/聊聊-mysql-的-MVCC-续篇/"},{"title":"聊聊 mysql 的 MVCC 续续篇之锁分析","url":"/2020/05/10/聊聊-mysql-的-MVCC-续续篇之加锁分析/"},{"title":"聊聊 redis 缓存的应用问题","url":"/2021/01/31/聊聊-redis-缓存的应用问题/"},{"title":"聊聊 mysql 的 MVCC","url":"/2020/04/26/聊聊-mysql-的-MVCC/"},{"title":"聊聊 mysql 索引的一些细节","url":"/2020/12/27/聊聊-mysql-索引的一些细节/"},{"title":"聊聊Java中的单例模式","url":"/2019/12/21/聊聊Java中的单例模式/"},{"title":"聊聊 SpringBoot 自动装配","url":"/2021/07/11/聊聊SpringBoot-自动装配/"},{"title":"聊聊传说中的 ThreadLocal","url":"/2021/05/30/聊聊传说中的-ThreadLocal/"},{"title":"聊聊一次 brew update 引发的血案","url":"/2020/06/13/聊聊一次-brew-update-引发的血案/"},{"title":"聊聊厦门旅游的好与不好","url":"/2021/04/11/聊聊厦门旅游的好与不好/"},{"title":"聊聊如何识别和意识到日常生活中的各类危险","url":"/2021/06/06/聊聊如何识别和意识到日常生活中的各类危险/"},{"title":"聊聊我理解的分布式事务","url":"/2020/05/17/聊聊我理解的分布式事务/"},{"title":"聊聊我刚学会的应用诊断方法","url":"/2020/05/22/聊聊我刚学会的应用诊断方法/"},{"title":"聊聊最近平淡的生活之又聊通勤","url":"/2021/11/07/聊聊最近平淡的生活/"},{"title":"聊聊 Sharding-Jdbc 分库分表下的分页方案","url":"/2022/01/09/聊聊-Sharding-Jdbc-分库分表下的分页方案/"},{"title":"聊聊最近平淡的生活之看《神探狄仁杰》","url":"/2021/12/19/聊聊最近平淡的生活之看《神探狄仁杰》/"},{"title":"聊聊最近平淡的生活之看看老剧","url":"/2021/11/21/聊聊最近平淡的生活之看看老剧/"},{"title":"聊聊给亲戚朋友的老电脑重装系统那些事儿","url":"/2021/05/09/聊聊给亲戚朋友的老电脑重装系统那些事儿/"},{"title":"聊聊那些加塞狗","url":"/2021/01/17/聊聊那些加塞狗/"},{"title":"聊聊部分公交车的设计bug","url":"/2021/12/05/聊聊部分公交车的设计bug/"},{"title":"这周末我又在老丈人家打了天小工","url":"/2020/08/30/这周末我又在老丈人家打了天小工/"},{"title":"重看了下《蛮荒记》说说感受","url":"/2021/10/10/重看了下《蛮荒记》说说感受/"},{"title":"闲聊下乘公交的用户体验","url":"/2021/02/28/闲聊下乘公交的用户体验/"},{"title":"聊聊最近平淡的生活之《花束般的恋爱》观后感","url":"/2021/12/31/聊聊最近平淡的生活之《花束般的恋爱》观后感/"},{"title":"聊聊这次换车牌及其他","url":"/2022/02/20/聊聊这次换车牌及其他/"}] \ No newline at end of file +[{"title":"村上春树《1Q84》读后感","url":"/2019/12/18/1Q84读后感/"},{"title":"2019年终总结","url":"/2020/02/01/2019年终总结/"},{"title":"2020 年终总结","url":"/2021/03/31/2020-年终总结/"},{"title":"2020年中总结","url":"/2020/07/11/2020年中总结/"},{"title":"34_Search_for_a_Range","url":"/2016/08/14/34-Search-for-a-Range/"},{"title":"AQS篇二 之 Condition 浅析笔记","url":"/2021/02/21/AQS-之-Condition-浅析笔记/"},{"title":"2021 年中总结","url":"/2021/07/18/2021-年中总结/"},{"title":"AbstractQueuedSynchronizer","url":"/2019/09/23/AbstractQueuedSynchronizer/"},{"title":"add-two-number","url":"/2015/04/14/Add-Two-Number/"},{"title":"AQS篇一","url":"/2021/02/14/AQS篇一/"},{"title":"2021 年终总结","url":"/2022/01/22/2021-年终总结/"},{"title":"Clone Graph Part I","url":"/2014/12/30/Clone-Graph-Part-I/"},{"title":"Apollo 的 value 注解是怎么自动更新的","url":"/2020/11/01/Apollo-的-value-注解是怎么自动更新的/"},{"title":"Comparator使用小记","url":"/2020/04/05/Comparator使用小记/"},{"title":"Disruptor 系列一","url":"/2022/02/13/Disruptor-系列一/"},{"title":"Disruptor 系列二","url":"/2022/02/27/Disruptor-系列二/"},{"title":"Filter, Interceptor, Aop, 啥, 啥, 啥? 这些都是啥?","url":"/2020/08/22/Filter-Intercepter-Aop-啥-啥-啥-这些都是啥/"},{"title":"G1收集器概述","url":"/2020/02/09/G1收集器概述/"},{"title":"JVM源码分析之G1垃圾收集器分析一","url":"/2019/12/07/JVM-G1-Part-1/"},{"title":"Leetcode 021 合并两个有序链表 ( Merge Two Sorted Lists ) 题解分析","url":"/2021/10/07/Leetcode-021-合并两个有序链表-Merge-Two-Sorted-Lists-题解分析/"},{"title":"Leetcode 028 实现 strStr() ( Implement strStr() ) 题解分析","url":"/2021/10/31/Leetcode-028-实现-strStr-Implement-strStr-题解分析/"},{"title":"Leetcode 053 最大子序和 ( Maximum Subarray ) 题解分析","url":"/2021/11/28/Leetcode-053-最大子序和-Maximum-Subarray-题解分析/"},{"title":"Leetcode 104 二叉树的最大深度(Maximum Depth of Binary Tree) 题解分析","url":"/2020/10/25/Leetcode-104-二叉树的最大深度-Maximum-Depth-of-Binary-Tree-题解分析/"},{"title":"Leetcode 105 从前序与中序遍历序列构造二叉树(Construct Binary Tree from Preorder and Inorder Traversal) 题解分析","url":"/2020/12/13/Leetcode-105-从前序与中序遍历序列构造二叉树-Construct-Binary-Tree-from-Preorder-and-Inorder-Traversal-题解分析/"},{"title":"Leetcode 121 买卖股票的最佳时机(Best Time to Buy and Sell Stock) 题解分析","url":"/2021/03/14/Leetcode-121-买卖股票的最佳时机-Best-Time-to-Buy-and-Sell-Stock-题解分析/"},{"title":"Leetcode 124 二叉树中的最大路径和(Binary Tree Maximum Path Sum) 题解分析","url":"/2021/01/24/Leetcode-124-二叉树中的最大路径和-Binary-Tree-Maximum-Path-Sum-题解分析/"},{"title":"Leetcode 155 最小栈(Min Stack) 题解分析","url":"/2020/12/06/Leetcode-155-最小栈-Min-Stack-题解分析/"},{"title":"Leetcode 160 相交链表(intersection-of-two-linked-lists) 题解分析","url":"/2021/01/10/Leetcode-160-相交链表-intersection-of-two-linked-lists-题解分析/"},{"title":"Leetcode 2 Add Two Numbers 题解分析","url":"/2020/10/11/Leetcode-2-Add-Two-Numbers-题解分析/"},{"title":"Leetcode 234 回文链表(Palindrome Linked List) 题解分析","url":"/2020/11/15/Leetcode-234-回文联表-Palindrome-Linked-List-题解分析/"},{"title":"Leetcode 236 二叉树的最近公共祖先(Lowest Common Ancestor of a Binary Tree) 题解分析","url":"/2021/05/23/Leetcode-236-二叉树的最近公共祖先-Lowest-Common-Ancestor-of-a-Binary-Tree-题解分析/"},{"title":"Leetcode 3 Longest Substring Without Repeating Characters 题解分析","url":"/2020/09/20/Leetcode-3-Longest-Substring-Without-Repeating-Characters-题解分析/"},{"title":"Leetcode 4 寻找两个正序数组的中位数 ( Median of Two Sorted Arrays *Hard* ) 题解分析","url":"/2022/03/27/Leetcode-4-寻找两个正序数组的中位数-Median-of-Two-Sorted-Arrays-Hard-题解分析/"},{"title":"Leetcode 42 接雨水 (Trapping Rain Water) 题解分析","url":"/2021/07/04/Leetcode-42-接雨水-Trapping-Rain-Water-题解分析/"},{"title":"Leetcode 48 旋转图像(Rotate Image) 题解分析","url":"/2021/05/01/Leetcode-48-旋转图像-Rotate-Image-题解分析/"},{"title":"Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析","url":"/2022/03/07/Leetcode-349-两个数组的交集-Intersection-of-Two-Arrays-Easy-题解分析/"},{"title":"Leetcode 83 删除排序链表中的重复元素 ( Remove Duplicates from Sorted List *Easy* ) 题解分析","url":"/2022/03/13/Leetcode-83-删除排序链表中的重复元素-Remove-Duplicates-from-Sorted-List-Easy-题解分析/"},{"title":"leetcode no.3","url":"/2015/04/15/Leetcode-No-3/"},{"title":"Linux 下 grep 命令的一点小技巧","url":"/2020/08/06/Linux-下-grep-命令的一点小技巧/"},{"title":"MFC 模态对话框","url":"/2014/12/24/MFC 模态对话框/"},{"title":"Maven实用小技巧","url":"/2020/02/16/Maven实用小技巧/"},{"title":"Number of 1 Bits","url":"/2015/03/11/Number-Of-1-Bits/"},{"title":"Path Sum","url":"/2015/01/04/Path-Sum/"},{"title":"Redis_分布式锁","url":"/2019/12/10/Redis-Part-1/"},{"title":"Reverse Bits","url":"/2015/03/11/Reverse-Bits/"},{"title":"Reverse Integer","url":"/2015/03/13/Reverse-Integer/"},{"title":"two sum","url":"/2015/01/14/Two-Sum/"},{"title":"ambari-summary","url":"/2017/05/09/ambari-summary/"},{"title":"binary-watch","url":"/2016/09/29/binary-watch/"},{"title":"docker-mysql-cluster","url":"/2016/08/14/docker-mysql-cluster/"},{"title":"docker比一般多一点的初学者介绍","url":"/2020/03/08/docker比一般多一点的初学者介绍/"},{"title":"docker比一般多一点的初学者介绍三","url":"/2020/03/21/docker比一般多一点的初学者介绍三/"},{"title":"docker比一般多一点的初学者介绍二","url":"/2020/03/15/docker比一般多一点的初学者介绍二/"},{"title":"docker使用中发现的echo命令的一个小技巧及其他","url":"/2020/03/29/echo命令的一个小技巧/"},{"title":"gogs使用webhook部署react单页应用","url":"/2020/02/22/gogs使用webhook部署react单页应用/"},{"title":"invert-binary-tree","url":"/2015/06/22/invert-binary-tree/"},{"title":"minimum-size-subarray-sum-209","url":"/2016/10/11/minimum-size-subarray-sum-209/"},{"title":"mybatis 的 $ 和 # 是有啥区别","url":"/2020/09/06/mybatis-的-和-是有啥区别/"},{"title":"C++ 指针使用中的一个小问题","url":"/2014/12/23/my-new-post/"},{"title":"mybatis 的缓存是怎么回事","url":"/2020/10/03/mybatis-的缓存是怎么回事/"},{"title":"openresty","url":"/2019/06/18/openresty/"},{"title":"pcre-intro-and-a-simple-package","url":"/2015/01/16/pcre-intro-and-a-simple-package/"},{"title":"php-abstract-class-and-interface","url":"/2016/11/10/php-abstract-class-and-interface/"},{"title":"rabbitmq-tips","url":"/2017/04/25/rabbitmq-tips/"},{"title":"redis 的 rdb 和 COW 介绍","url":"/2021/08/15/redis-的-rdb-和-COW-介绍/"},{"title":"redis数据结构介绍-第一部分 SDS,链表,字典","url":"/2019/12/26/redis数据结构介绍/"},{"title":"redis数据结构介绍三-第三部分 整数集合","url":"/2020/01/10/redis数据结构介绍三/"},{"title":"redis数据结构介绍二-第二部分 跳表","url":"/2020/01/04/redis数据结构介绍二/"},{"title":"redis数据结构介绍五-第五部分 对象","url":"/2020/01/20/redis数据结构介绍五/"},{"title":"redis数据结构介绍六 快表","url":"/2020/01/22/redis数据结构介绍六/"},{"title":"redis数据结构介绍四-第四部分 压缩表","url":"/2020/01/19/redis数据结构介绍四/"},{"title":"redis淘汰策略复习","url":"/2021/08/01/redis淘汰策略复习/"},{"title":"redis系列介绍七-过期策略","url":"/2020/04/12/redis系列介绍七/"},{"title":"redis系列介绍八-淘汰策略","url":"/2020/04/18/redis系列介绍八/"},{"title":"redis过期策略复习","url":"/2021/07/25/redis过期策略复习/"},{"title":"rust学习笔记-所有权三之切片","url":"/2021/05/16/rust学习笔记-所有权三之切片/"},{"title":"rust学习笔记-所有权二","url":"/2021/04/18/rust学习笔记-所有权二/"},{"title":"spark-little-tips","url":"/2017/03/28/spark-little-tips/"},{"title":"rust学习笔记-所有权一","url":"/2021/04/18/rust学习笔记/"},{"title":"spring event 介绍","url":"/2022/01/30/spring-event-介绍/"},{"title":"summary-ranges-228","url":"/2016/10/12/summary-ranges-228/"},{"title":"wordpress 忘记密码的一种解决方法","url":"/2021/12/05/wordpress-忘记密码的一种解决方法/"},{"title":"swoole-websocket-test","url":"/2016/07/13/swoole-websocket-test/"},{"title":"《垃圾回收算法手册读书》笔记之整理算法","url":"/2021/03/07/《垃圾回收算法手册读书》笔记之整理算法/"},{"title":"上次的其他 外行聊国足","url":"/2022/03/06/上次的其他-外行聊国足/"},{"title":"介绍一下 RocketMQ","url":"/2020/06/21/介绍一下-RocketMQ/"},{"title":"介绍下最近比较实用的端口转发","url":"/2021/11/14/介绍下最近比较实用的端口转发/"},{"title":"从丁仲礼被美国制裁聊点啥","url":"/2020/12/20/从丁仲礼被美国制裁聊点啥/"},{"title":"从清华美院学姐聊聊我们身边的恶人","url":"/2020/11/29/从清华美院学姐聊聊我们身边的恶人/"},{"title":"关于读书打卡与分享","url":"/2021/02/07/关于读书打卡与分享/"},{"title":"关于公共交通再吐个槽","url":"/2021/03/21/关于公共交通再吐个槽/"},{"title":"周末我在老丈人家打了天小工","url":"/2020/08/16/周末我在老丈人家打了天小工/"},{"title":"分享记录一下一个 scp 操作方法","url":"/2022/02/06/分享记录一下一个-scp-操作方法/"},{"title":"分享记录一下一个 git 操作方法","url":"/2022/02/06/分享记录一下一个-git-操作方法/"},{"title":"在老丈人家的小工记三","url":"/2020/09/13/在老丈人家的小工记三/"},{"title":"在老丈人家的小工记五","url":"/2020/10/18/在老丈人家的小工记五/"},{"title":"在老丈人家的小工记四","url":"/2020/09/26/在老丈人家的小工记四/"},{"title":"寄生虫观后感","url":"/2020/03/01/寄生虫观后感/"},{"title":"我是如何走上跑步这条不归路的","url":"/2020/07/26/我是如何走上跑步这条不归路的/"},{"title":"看完了扫黑风暴,聊聊感想","url":"/2021/10/24/看完了扫黑风暴-聊聊感想/"},{"title":"搬运两个 StackOverflow 上的 Mysql 编码相关的问题解答","url":"/2022/01/16/搬运两个-StackOverflow-上的-Mysql-编码相关的问题解答/"},{"title":"聊一下 RocketMQ 的 DefaultMQPushConsumer 源码","url":"/2020/06/26/聊一下-RocketMQ-的-Consumer/"},{"title":"聊一下 RocketMQ 的 NameServer 源码","url":"/2020/07/05/聊一下-RocketMQ-的-NameServer-源码/"},{"title":"聊一下 RocketMQ 的消息存储之 MMAP","url":"/2021/09/04/聊一下-RocketMQ-的消息存储/"},{"title":"给小电驴上牌","url":"/2022/03/20/给小电驴上牌/"},{"title":"聊一下 RocketMQ 的消息存储三","url":"/2021/10/03/聊一下-RocketMQ-的消息存储三/"},{"title":"聊一下 RocketMQ 的消息存储二","url":"/2021/09/12/聊一下-RocketMQ-的消息存储二/"},{"title":"聊一下 RocketMQ 的消息存储四","url":"/2021/10/17/聊一下-RocketMQ-的消息存储四/"},{"title":"聊一下 RocketMQ 的顺序消息","url":"/2021/08/29/聊一下-RocketMQ-的顺序消息/"},{"title":"聊一下 SpringBoot 中使用的 cglib 作为动态代理中的一个注意点","url":"/2021/09/19/聊一下-SpringBoot-中使用的-cglib-作为动态代理中的一个注意点/"},{"title":"聊一下 SpringBoot 中动态切换数据源的方法","url":"/2021/09/26/聊一下-SpringBoot-中动态切换数据源的方法/"},{"title":"聊在东京奥运会闭幕式这天","url":"/2021/08/08/聊在东京奥运会闭幕式这天/"},{"title":"聊在东京奥运会闭幕式这天-二","url":"/2021/08/19/聊在东京奥运会闭幕式这天-二/"},{"title":"聊聊 Dubbo 的 SPI 续之自适应拓展","url":"/2020/06/06/聊聊-Dubbo-的-SPI-续之自适应拓展/"},{"title":"聊聊 Dubbo 的 SPI","url":"/2020/05/31/聊聊-Dubbo-的-SPI/"},{"title":"聊聊 Dubbo 的容错机制","url":"/2020/11/22/聊聊-Dubbo-的容错机制/"},{"title":"聊聊 Java 中绕不开的 Synchronized 关键字-二","url":"/2021/06/27/聊聊-Java-中绕不开的-Synchronized-关键字-二/"},{"title":"聊聊 Java 的 equals 和 hashCode 方法","url":"/2021/01/03/聊聊-Java-的-equals-和-hashCode-方法/"},{"title":"聊聊 Java 的类加载机制一","url":"/2020/11/08/聊聊-Java-的类加载机制/"},{"title":"聊聊 Java 的类加载机制二","url":"/2021/06/13/聊聊-Java-的类加载机制二/"},{"title":"聊聊 Java 中绕不开的 Synchronized 关键字","url":"/2021/06/20/聊聊-Java-中绕不开的-Synchronized-关键字/"},{"title":"聊聊 Linux 下的 top 命令","url":"/2021/03/28/聊聊-Linux-下的-top-命令/"},{"title":"聊聊 RocketMQ 的 Broker 源码","url":"/2020/07/19/聊聊-RocketMQ-的-Broker-源码/"},{"title":"聊聊 Java 自带的那些*逆天*工具","url":"/2020/08/02/聊聊-Java-自带的那些逆天工具/"},{"title":"聊聊 Sharding-Jdbc 的简单使用","url":"/2021/12/12/聊聊-Sharding-Jdbc-的简单使用/"},{"title":"聊聊 dubbo 的线程池","url":"/2021/04/04/聊聊-dubbo-的线程池/"},{"title":"聊聊 mysql 的 MVCC 续篇","url":"/2020/05/02/聊聊-mysql-的-MVCC-续篇/"},{"title":"聊聊 mysql 的 MVCC 续续篇之锁分析","url":"/2020/05/10/聊聊-mysql-的-MVCC-续续篇之加锁分析/"},{"title":"聊聊 Sharding-Jdbc 的简单原理初篇","url":"/2021/12/26/聊聊-Sharding-Jdbc-的简单原理初篇/"},{"title":"聊聊 mysql 的 MVCC","url":"/2020/04/26/聊聊-mysql-的-MVCC/"},{"title":"聊聊 Sharding-Jdbc 分库分表下的分页方案","url":"/2022/01/09/聊聊-Sharding-Jdbc-分库分表下的分页方案/"},{"title":"聊聊 mysql 索引的一些细节","url":"/2020/12/27/聊聊-mysql-索引的一些细节/"},{"title":"聊聊 redis 缓存的应用问题","url":"/2021/01/31/聊聊-redis-缓存的应用问题/"},{"title":"聊聊 SpringBoot 自动装配","url":"/2021/07/11/聊聊SpringBoot-自动装配/"},{"title":"聊聊一次 brew update 引发的血案","url":"/2020/06/13/聊聊一次-brew-update-引发的血案/"},{"title":"聊聊厦门旅游的好与不好","url":"/2021/04/11/聊聊厦门旅游的好与不好/"},{"title":"聊聊传说中的 ThreadLocal","url":"/2021/05/30/聊聊传说中的-ThreadLocal/"},{"title":"聊聊如何识别和意识到日常生活中的各类危险","url":"/2021/06/06/聊聊如何识别和意识到日常生活中的各类危险/"},{"title":"聊聊我刚学会的应用诊断方法","url":"/2020/05/22/聊聊我刚学会的应用诊断方法/"},{"title":"聊聊我理解的分布式事务","url":"/2020/05/17/聊聊我理解的分布式事务/"},{"title":"聊聊最近平淡的生活之又聊通勤","url":"/2021/11/07/聊聊最近平淡的生活/"},{"title":"聊聊最近平淡的生活之看《神探狄仁杰》","url":"/2021/12/19/聊聊最近平淡的生活之看《神探狄仁杰》/"},{"title":"聊聊最近平淡的生活之《花束般的恋爱》观后感","url":"/2021/12/31/聊聊最近平淡的生活之《花束般的恋爱》观后感/"},{"title":"聊聊最近平淡的生活之看看老剧","url":"/2021/11/21/聊聊最近平淡的生活之看看老剧/"},{"title":"聊聊给亲戚朋友的老电脑重装系统那些事儿","url":"/2021/05/09/聊聊给亲戚朋友的老电脑重装系统那些事儿/"},{"title":"聊聊这次换车牌及其他","url":"/2022/02/20/聊聊这次换车牌及其他/"},{"title":"聊聊那些加塞狗","url":"/2021/01/17/聊聊那些加塞狗/"},{"title":"聊聊部分公交车的设计bug","url":"/2021/12/05/聊聊部分公交车的设计bug/"},{"title":"这周末我又在老丈人家打了天小工","url":"/2020/08/30/这周末我又在老丈人家打了天小工/"},{"title":"重看了下《蛮荒记》说说感受","url":"/2021/10/10/重看了下《蛮荒记》说说感受/"},{"title":"闲聊下乘公交的用户体验","url":"/2021/02/28/闲聊下乘公交的用户体验/"},{"title":"聊聊Java中的单例模式","url":"/2019/12/21/聊聊Java中的单例模式/"}] \ No newline at end of file diff --git a/page/12/index.html b/page/12/index.html index c2b7bc9b69..3c6e80bd1f 100644 --- a/page/12/index.html +++ b/page/12/index.html @@ -1,4 +1,4 @@ -Nicksxs's Blog 0%聊聊 dubbo 的线程池
之前没注意到这一块,只是比较模糊的印象 dubbo 自己基于 ThreadPoolExecutor 定义了几个线程池,但是没具体看过,主要是觉得就是为了避免使用 jdk 自带的那几个(java.util.concurrent.Executors),防止出现那些问题
看下代码目录主要是这几个![]()
- FixedThreadPool:创建一个复用固定个数线程的线程池。
简单看下代码public Executor getExecutor(URL url) { +Nicksxs's Blog 0%聊聊 dubbo 的线程池
之前没注意到这一块,只是比较模糊的印象 dubbo 自己基于 ThreadPoolExecutor 定义了几个线程池,但是没具体看过,主要是觉得就是为了避免使用 jdk 自带的那几个(java.util.concurrent.Executors),防止出现那些问题
看下代码目录主要是这几个![]()
- FixedThreadPool:创建一个复用固定个数线程的线程池。
简单看下代码
+public Executor getExecutor(URL url) { String name = url.getParameter("threadname", "Dubbo"); int threads = url.getParameter("threads", 200); int queues = url.getParameter("queues", 0); diff --git a/search.xml b/search.xml index ec048d9a1f..d5ca648f3e 100644 --- a/search.xml +++ b/search.xml @@ -20,6 +20,35 @@读后感 ++ 2019年终总结 +/2020/02/01/2019%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ +今天是农历初八了,年前一个月的时候就准备做下今年的年终总结,可是写了一点觉得太情绪化了,希望后面写个平淡点的,正好最近技术方面还没有看到一个完整成文的内容,就来写一下这一年的总结,尽量少写一点太情绪化的东西。 + +跳槽
年初换了个公司,也算换了个环境,跟前公司不太一样,做的事情方向也不同,可能是侧重点不同,一开始有些不适应,主要是压力上,会觉得压力比较大,但是总体来说与人相处的部分还是不错的,做的技术方向还是Java,这里也感谢前东家让我有机会转了Java,个人感觉杭州整个市场还是Java比较有优势,不过在开始的时候总觉得对Java有点不适应,应该值得深究的东西还是很多的,而且对于面试来说,也是有很多可以问的,后面慢慢发现除开某里等一线超一线互联网公司之外,大部分的面试还是有大概的套路跟大纲的,不过更细致的则因人而异了,面试有时候也还看缘分,面试官关注的点跟应试者比较契合的话就很容易通过面试,不然的话总会有能刁难或者理性化地说比较难回答的问题。这个后面可以单独说一下,先按下不表。
+
刚进公司没多久就负责比较重要的项目,工期也比较紧张,整体来说那段时间的压力的确是比较大的,不过总算最后结果不坏,这里应该说对一些原来在前东家都是掌握的不太好的部分,比如maven,其实maven对于java程序员来说还是很重要的,但是我碰到过的面试基本没问过这个,我自己也在后面的面试中没问过相关的,不知道咋问,比如dependence分析、冲突解决,比如对bean的理解,这个算是我一直以来的疑问点,因为以前刚开始学Java学spring,上来就是bean,但是bean到底是啥,IOC是啥,可能网上的文章跟大多数书籍跟我的理解思路不太match,导致一直不能很好的理解这玩意,到后面才理解,要理解这个bean,需要有两个基本概念,一个是面向对象,一个是对象容器跟依赖反转,还是只说到这,后面可以有专题说一下,总之自认为技术上有了不小的长进了,方向上应该是偏实用的。这个重要的项目完成后慢慢能喘口气了,后面也有一些比较紧急且工作量大的,不过在我TL的帮助下还是能尽量协调好资源。面试
后面因为项目比较多,缺少开发,所以也参与帮忙做一些面试,这里总体感觉是面的候选人还是比较多样的,有些工作了蛮多年但是一些基础问题回答的不好,有些还是在校学生,但是面试技巧不错,针对常见的面试题都有不错的准备,不过还是觉得光靠这些面试题不能完全说明问题,真正工作了需要的是解决问题的人,而不是会背题的,退一步来说能好好准备面试还是比较重要的,也是双向选择中的基本尊重,印象比较深刻的是参加了去杭州某高校的校招面试,感觉参加校招的同学还是很多的,大部分是20年将毕业的研究生,挺多都是基础很扎实,对比起我刚要毕业时还是很汗颜,挺多来面试的同学都非常不错,那天强度也很大,从下午到那开始一直面到六七点,在这祝福那些来面试的同学,也都不容易的,能找到心仪的工作。
+技术方向
这一年前大半部分还是比较焦虑不能恢复那种主动找时间学习的状态,可能换了公司是主要的原因,初期有个适应的过程也比较正常,总体来说可能是到九十月份开始慢慢有所改善,对这些方面有学习了下,
+-
+
- spring方向,spring真的是个庞然大物,但是还是要先抓住根本,慢慢发散去了解其他的细节,抓住bean的生命周期,当然也不是死记硬背,让我一个个背下来我也不行,但是知道它究竟是干嘛的,有啥用,并且在工作中能用起来是最重要的 +
- mysql数据库,这部分主要是关注了mvcc,知道了个大概,源码实现细节还没具体研究,有时间可以来个专题(一大堆待写的内容) +
- java的一些源码,比如aqs这种,结合文章看了下源码,一开始总感觉静不下心来看,然后有一次被LD刺激了下就看完了,包括conditionObject等 +
- redis的源码,这里包括了Redis分布式锁和redis的数据结构源码,已经写成文章,不过比较着急成文,所以质量不是特别好,希望后面再来补补 +
- jvm源码,这部分正好是想了解下g1收集器,大概把周志明的书看完了,但是还没完整的理解掌握,还有就是g1收集器的部分,一是概念部分大概理解了,后面是就是想从源码层面去学习理解,这也是新一年的主要计划 +
- mq的部分是了解了zero copy,sendfile等,跟消息队列主题关系不大🤦♂️
这么看还是学了点东西的,希望新一年再接再厉。
+
生活
住的地方没变化,主要是周边设施比较方便,暂时没找到更好的就没打算换,主要的问题是没电梯,一开始没觉得有啥,真正住起来还是觉得比较累的,希望后面租的可以有电梯,或者楼层低一点,还有就是要通下水道,第一次让师傅上门,花了两百大洋,后来自学成才了,让师傅通了一次才撑了一个月就不行了,后面自己通的差不多可以撑半年,还是比较有成就感的😀,然后就是跑步了,年初的时候去了紫金港跑步,后面因为工作的原因没去了,但是公司的跑步机倒是让我重拾起这个唯一的运动健身项目,后面因为肠胃问题,体重也需要控制,所以就周末回来也在家这边坚持跑步,下半年的话基本保持每周一次以上,比较那些跑马拉松的大牛还是差距很大,不过也是突破自我了,有一次跑了12公里,最远的距离,而且后面感觉跑十公里也不是特别吃不消了,这一年达成了300公里的目标,体重也稍有下降,比较满意的结果。
+期待
希望工作方面技术方面能有所长进,生活上能多点时间陪家人,继续跑步减肥,家人健健康康的,嗯
+]]>+ +生活 +年终总结 +2019 ++ +生活 +年终总结 +2019 + -2020 年终总结 /2021/03/31/2020-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ @@ -64,35 +93,6 @@年中总结 - 2019年终总结 -/2020/02/01/2019%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ -今天是农历初八了,年前一个月的时候就准备做下今年的年终总结,可是写了一点觉得太情绪化了,希望后面写个平淡点的,正好最近技术方面还没有看到一个完整成文的内容,就来写一下这一年的总结,尽量少写一点太情绪化的东西。 - -跳槽
年初换了个公司,也算换了个环境,跟前公司不太一样,做的事情方向也不同,可能是侧重点不同,一开始有些不适应,主要是压力上,会觉得压力比较大,但是总体来说与人相处的部分还是不错的,做的技术方向还是Java,这里也感谢前东家让我有机会转了Java,个人感觉杭州整个市场还是Java比较有优势,不过在开始的时候总觉得对Java有点不适应,应该值得深究的东西还是很多的,而且对于面试来说,也是有很多可以问的,后面慢慢发现除开某里等一线超一线互联网公司之外,大部分的面试还是有大概的套路跟大纲的,不过更细致的则因人而异了,面试有时候也还看缘分,面试官关注的点跟应试者比较契合的话就很容易通过面试,不然的话总会有能刁难或者理性化地说比较难回答的问题。这个后面可以单独说一下,先按下不表。
-
刚进公司没多久就负责比较重要的项目,工期也比较紧张,整体来说那段时间的压力的确是比较大的,不过总算最后结果不坏,这里应该说对一些原来在前东家都是掌握的不太好的部分,比如maven,其实maven对于java程序员来说还是很重要的,但是我碰到过的面试基本没问过这个,我自己也在后面的面试中没问过相关的,不知道咋问,比如dependence分析、冲突解决,比如对bean的理解,这个算是我一直以来的疑问点,因为以前刚开始学Java学spring,上来就是bean,但是bean到底是啥,IOC是啥,可能网上的文章跟大多数书籍跟我的理解思路不太match,导致一直不能很好的理解这玩意,到后面才理解,要理解这个bean,需要有两个基本概念,一个是面向对象,一个是对象容器跟依赖反转,还是只说到这,后面可以有专题说一下,总之自认为技术上有了不小的长进了,方向上应该是偏实用的。这个重要的项目完成后慢慢能喘口气了,后面也有一些比较紧急且工作量大的,不过在我TL的帮助下还是能尽量协调好资源。面试
后面因为项目比较多,缺少开发,所以也参与帮忙做一些面试,这里总体感觉是面的候选人还是比较多样的,有些工作了蛮多年但是一些基础问题回答的不好,有些还是在校学生,但是面试技巧不错,针对常见的面试题都有不错的准备,不过还是觉得光靠这些面试题不能完全说明问题,真正工作了需要的是解决问题的人,而不是会背题的,退一步来说能好好准备面试还是比较重要的,也是双向选择中的基本尊重,印象比较深刻的是参加了去杭州某高校的校招面试,感觉参加校招的同学还是很多的,大部分是20年将毕业的研究生,挺多都是基础很扎实,对比起我刚要毕业时还是很汗颜,挺多来面试的同学都非常不错,那天强度也很大,从下午到那开始一直面到六七点,在这祝福那些来面试的同学,也都不容易的,能找到心仪的工作。
-技术方向
这一年前大半部分还是比较焦虑不能恢复那种主动找时间学习的状态,可能换了公司是主要的原因,初期有个适应的过程也比较正常,总体来说可能是到九十月份开始慢慢有所改善,对这些方面有学习了下,
--
-
- spring方向,spring真的是个庞然大物,但是还是要先抓住根本,慢慢发散去了解其他的细节,抓住bean的生命周期,当然也不是死记硬背,让我一个个背下来我也不行,但是知道它究竟是干嘛的,有啥用,并且在工作中能用起来是最重要的 -
- mysql数据库,这部分主要是关注了mvcc,知道了个大概,源码实现细节还没具体研究,有时间可以来个专题(一大堆待写的内容) -
- java的一些源码,比如aqs这种,结合文章看了下源码,一开始总感觉静不下心来看,然后有一次被LD刺激了下就看完了,包括conditionObject等 -
- redis的源码,这里包括了Redis分布式锁和redis的数据结构源码,已经写成文章,不过比较着急成文,所以质量不是特别好,希望后面再来补补 -
- jvm源码,这部分正好是想了解下g1收集器,大概把周志明的书看完了,但是还没完整的理解掌握,还有就是g1收集器的部分,一是概念部分大概理解了,后面是就是想从源码层面去学习理解,这也是新一年的主要计划 -
- mq的部分是了解了zero copy,sendfile等,跟消息队列主题关系不大🤦♂️
这么看还是学了点东西的,希望新一年再接再厉。
-
生活
住的地方没变化,主要是周边设施比较方便,暂时没找到更好的就没打算换,主要的问题是没电梯,一开始没觉得有啥,真正住起来还是觉得比较累的,希望后面租的可以有电梯,或者楼层低一点,还有就是要通下水道,第一次让师傅上门,花了两百大洋,后来自学成才了,让师傅通了一次才撑了一个月就不行了,后面自己通的差不多可以撑半年,还是比较有成就感的😀,然后就是跑步了,年初的时候去了紫金港跑步,后面因为工作的原因没去了,但是公司的跑步机倒是让我重拾起这个唯一的运动健身项目,后面因为肠胃问题,体重也需要控制,所以就周末回来也在家这边坚持跑步,下半年的话基本保持每周一次以上,比较那些跑马拉松的大牛还是差距很大,不过也是突破自我了,有一次跑了12公里,最远的距离,而且后面感觉跑十公里也不是特别吃不消了,这一年达成了300公里的目标,体重也稍有下降,比较满意的结果。
-期待
希望工作方面技术方面能有所长进,生活上能多点时间陪家人,继续跑步减肥,家人健健康康的,嗯
-]]>- -生活 -年终总结 -2019 -- -生活 -年终总结 -2019 - -34_Search_for_a_Range /2016/08/14/34-Search-for-a-Range/ @@ -139,29 +139,6 @@ public:c++ - 2021 年中总结 -/2021/07/18/2021-%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/ -又到半年总结时,第一次写总结类型的文章感觉挺好写的,但是后面总觉得这过去的一段时间所做的事情,能力上的成长低于预期,但是是需要总结下,找找问题,顺便展望下未来。 - -这一年做的最让自己满意的应该就是看了一些书,由折腾群洋总发起的读书打卡活动,到目前为止已经读完了这几本书,《cUrl 必知必会》,《古董局中局 1》,《古董局中局 2》,《算法图解》,《每天 5 分钟玩转 Kubernetes》《幸福了吗?》《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud和 Service Mesh》《Rust 权威指南》后面可以写个专题说说看的这些书,虽然每天打卡如果时间安排不好,并且看的书像 rust 这样比较难的话还是会有点小焦虑,不过也是个调整过程,一方面可以在白天就抽空看一会,然后也不必要每次都看很大一章,注重吸收。
-技术上的成长的话,有一些比较小的长进吧,对于一些之前忽视的 synchronized,ThreadLocal 和 AQS 等知识点做了下查漏补缺了,然后多了解了一些 Java 垃圾回收的内容,但是在实操上还是比较欠缺,成型的技术方案,架构上所谓的优化也比较少,一些想法也还有考虑不周全的地方,还需要多花时间和心思去学习加强,特别是在目前已经有的基础上如何做系统深层次的优化,既不要是鸡毛蒜皮的,也不能出现一些不可接受的问题和故障,这是个很重要的课题,需要好好学习,后面考虑定一些周期性目标,两个月左右能有一些成果和总结。
-另外一部分是自己的服务,因为 ucloud 的机器太贵就没续费了,所以都迁移到腾讯云的小机器上了,顺便折腾了一点点 traefik,但是还很不熟练,不太习惯这一套,一方面是 docker 还不习惯,这也加重了对这套环境的不适应,还是习惯裸机部署,另一方面就是 k8s 了,家里的机器还没虚拟化,没有很好的条件可以做实验,这也是读书打卡的一个没做好的点,整体的学习效果受限于深度和实操,后面是看都是用 traefik,也找到了一篇文章可以 traefik 转发到裸机应用,因为主仓库用的是裸机的 gogs。
-还有就是运动减肥上,唉,这又是很大的一个痛点,基本没效果,只是还算稳定,昨天看到一个视频说还需要力量训练来增肌,以此可以提升基础代谢,打算往这个方向尝试下,因为今天没有疫情限制了,在 6 月底完成了 200 公里的跑步小目标,只是有些膝盖跟大腿根外侧不适,抽空得去看下医生,后面打算每天也能做点卷腹跟俯卧撑。
-下半年还希望能继续多看看书,比很多网上各种乱七八糟的文章会好很多,结合豆瓣评分,找一些评价高一些的文章,但也不是说分稍低点的就不行,有些也看人是不是适合,一般 6 分以上评价比较多的就可以试试。
-]]>- -生活 -年中总结 -2021 -- -生活 -2021 -年中总结 -技术 -读书 -AQS篇二 之 Condition 浅析笔记 /2021/02/21/AQS-%E4%B9%8B-Condition-%E6%B5%85%E6%9E%90%E7%AC%94%E8%AE%B0/ @@ -660,288 +637,66 @@ public:- 2021 年终总结 -/2022/01/22/2021-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ -又是一年年终总结,本着极度讨厌实时需求的理念,我还是 T+N 发布这个年终总结 - 工作篇
工作没什么大变化,有了些微的提升,可能因为是来了之后做了些项目对比公司与来还算是比较重要的,但是技术难度上没有特别突出的点,可能最开始用 openresty+lua 做了个 ab 测的工具,还是让我比较满意的,后面一般都是业务型的需求,今年可能在业务相关的技术逻辑上有了一些深度的了解,而原来一直想做的业务架构升级和通用型技术中间件这样的优化还是停留在想象中,前面说的 ab 测应该算是个半成品,还是没能多走出这一步,得需要多做一些实在的事情,比如轻量级的业务框架,能够对原先不熟悉的业务逻辑,代码逻辑有比较深入的理解,而不是一直都是让特定的同学负责特定的逻辑,很多时候还是在偷懒,习惯以一些简单安全的方案去做事情,在技术上还是要有所追求,还有就是能够在新语言,主要是 rust,swift 这类的能有些小玩具可以做,rust 的话是因为今年看了一本相关的书,后面三分之一其实消化得不好,这本书整体来说是很不错的,只是 rust 本身在所有权这块,还有引用包装等方面是设计得比较难懂,也可能是我基础差,所以还是想在复习下,可以做一个简单的命令行工具这种,然后 swift 是想说可以做点 mac 的小软件,原生的毕竟性能好点,又小。基于 web 做的客户端大部分都是又丑又大,极少数能好看点,但也是很重,起码 7~80M 的大小,原生的估计能除以 10。
+
整体的职业规划貌似陷入了比较大的困惑期,在目前公司发展前景不是很大,但是出去貌似也没有比较适合我的机会,总的来说还是杭州比较卷,个人觉得有自己的时间是非常重要的,而且这个不光是用来自我提升的,还是让自己有足够的时间做缓冲,有足够的时间锻炼减肥,时间少的情况下,不光会在仅有的时间里暴饮暴食,还没空锻炼,身体是革命的本钱,现在其实能特别明显地感觉到身体状态下滑,容易疲劳,焦虑。所以是否也许有可能以后要往外企这类的方向去发展。
工作上其实还是有个不大不小的缺点,就是容易激动,容易焦虑,前一点可能有稍稍地改观,因为工作中的很多现状其实是我个人难以改变的,即使觉得不合理,但是结构在那里,还不如自己放宽心,尽量做好事情就行。第二点的话还是做得比较差,一直以来抗压能力都比较差,跟成长环境,家庭环境都有比较大的关系,而且说实在的特别是父母,基本也没有在这方面给我正向的帮助,比较擅长给我施压,从小就是通过压力让我好好读书,当个乖学生,考个好学校,并没有能真正地理解我的压力,教我或者帮助我解压,只会在那说着不着边际的空话,甚至经常反过来对我施压。还是希望能慢慢解开,这点可能对我身体也有影响,也许需要看一些心理疏导相关的书籍。工作篇暂时到这,后续还有其他篇,未完待续哈哈😀2021 年中总结 +/2021/07/18/2021-%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/ +又到半年总结时,第一次写总结类型的文章感觉挺好写的,但是后面总觉得这过去的一段时间所做的事情,能力上的成长低于预期,但是是需要总结下,找找问题,顺便展望下未来。 + 这一年做的最让自己满意的应该就是看了一些书,由折腾群洋总发起的读书打卡活动,到目前为止已经读完了这几本书,《cUrl 必知必会》,《古董局中局 1》,《古董局中局 2》,《算法图解》,《每天 5 分钟玩转 Kubernetes》《幸福了吗?》《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud和 Service Mesh》《Rust 权威指南》后面可以写个专题说说看的这些书,虽然每天打卡如果时间安排不好,并且看的书像 rust 这样比较难的话还是会有点小焦虑,不过也是个调整过程,一方面可以在白天就抽空看一会,然后也不必要每次都看很大一章,注重吸收。
+技术上的成长的话,有一些比较小的长进吧,对于一些之前忽视的 synchronized,ThreadLocal 和 AQS 等知识点做了下查漏补缺了,然后多了解了一些 Java 垃圾回收的内容,但是在实操上还是比较欠缺,成型的技术方案,架构上所谓的优化也比较少,一些想法也还有考虑不周全的地方,还需要多花时间和心思去学习加强,特别是在目前已经有的基础上如何做系统深层次的优化,既不要是鸡毛蒜皮的,也不能出现一些不可接受的问题和故障,这是个很重要的课题,需要好好学习,后面考虑定一些周期性目标,两个月左右能有一些成果和总结。
+另外一部分是自己的服务,因为 ucloud 的机器太贵就没续费了,所以都迁移到腾讯云的小机器上了,顺便折腾了一点点 traefik,但是还很不熟练,不太习惯这一套,一方面是 docker 还不习惯,这也加重了对这套环境的不适应,还是习惯裸机部署,另一方面就是 k8s 了,家里的机器还没虚拟化,没有很好的条件可以做实验,这也是读书打卡的一个没做好的点,整体的学习效果受限于深度和实操,后面是看都是用 traefik,也找到了一篇文章可以 traefik 转发到裸机应用,因为主仓库用的是裸机的 gogs。
+还有就是运动减肥上,唉,这又是很大的一个痛点,基本没效果,只是还算稳定,昨天看到一个视频说还需要力量训练来增肌,以此可以提升基础代谢,打算往这个方向尝试下,因为今天没有疫情限制了,在 6 月底完成了 200 公里的跑步小目标,只是有些膝盖跟大腿根外侧不适,抽空得去看下医生,后面打算每天也能做点卷腹跟俯卧撑。
+下半年还希望能继续多看看书,比很多网上各种乱七八糟的文章会好很多,结合豆瓣评分,找一些评价高一些的文章,但也不是说分稍低点的就不行,有些也看人是不是适合,一般 6 分以上评价比较多的就可以试试。
]]>生活 -年终总结 +年中总结 +2021 生活 -年终总结 2021 -拖更 +年中总结 +技术 +读书 - -AQS篇一 -/2021/02/14/AQS%E7%AF%87%E4%B8%80/ -很多东西都是时看时新,而且时间长了也会忘,所以再来复习下,也会有一些新的角度看法这次来聊下AQS的内容,主要是这几个点, - 第一个线程
第一个线程抢到锁了,此时state跟阻塞队列是怎么样的,其实这里是之前没理解对的地方
-
+/** - * Fair version of tryAcquire. Don't grant access unless - * recursive call or no waiters or is first. - */ - protected final boolean tryAcquire(int acquires) { - final Thread current = Thread.currentThread(); - int c = getState(); - // 这里如果state还是0说明锁还空着 - if (c == 0) { - // 因为是公平锁版本的,先去看下是否阻塞队列里有排着队的 - if (!hasQueuedPredecessors() && - compareAndSetState(0, acquires)) { - // 没有排队的,并且state使用cas设置成功的就标记当前占有锁的线程是我 - setExclusiveOwnerThread(current); - // 然后其实就返回了,包括阻塞队列的head和tail节点和waitStatus都没有设置 - return true; - } - } - else if (current == getExclusiveOwnerThread()) { - int nextc = c + acquires; - if (nextc < 0) - throw new Error("Maximum lock count exceeded"); - setState(nextc); - return true; - } - // 这里就是第二个线程会返回false - return false; - } - }AbstractQueuedSynchronizer +/2019/09/23/AbstractQueuedSynchronizer/ +最近看了大神的 AQS 的文章,之前总是断断续续地看一点,每次都知难而退,下次看又从头开始,昨天总算硬着头皮看完了第一部分 -
首先 AQS 只要有这些属性 +
+// 头结点,你直接把它当做 当前持有锁的线程 可能是最好理解的 +private transient volatile Node head; -第二个线程
当第二个线程进来的时候应该是怎么样,结合代码来看
-
+// 阻塞的尾节点,每个新的节点进来,都插入到最后,也就形成了一个链表 +private transient volatile Node tail; -/** - * Acquires in exclusive mode, ignoring interrupts. Implemented - * by invoking at least once {@link #tryAcquire}, - * returning on success. Otherwise the thread is queued, possibly - * repeatedly blocking and unblocking, invoking {@link - * #tryAcquire} until success. This method can be used - * to implement method {@link Lock#lock}. - * - * @param arg the acquire argument. This value is conveyed to - * {@link #tryAcquire} but is otherwise uninterpreted and - * can represent anything you like. - */ - public final void acquire(int arg) { - // 前面第一种情况是tryAcquire直接成功了,这个if判断第一个条件就是false,就不往下执行了 - // 如果是第二个线程,第一个条件获取锁不成功,条件判断!tryAcquire(arg) == true,就会走 - // acquireQueued(addWaiter(Node.EXCLUSIVE), arg) - if (!tryAcquire(arg) && - acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) - selfInterrupt(); - }然后来看下addWaiter的逻辑
-
+// 这个是最重要的,代表当前锁的状态,0代表没有被占用,大于 0 代表有线程持有当前锁 +// 这个值可以大于 1,是因为锁可以重入,每次重入都加上 1 +private volatile int state; -/** - * Creates and enqueues node for current thread and given mode. - * - * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared - * @return the new node - */ - private Node addWaiter(Node mode) { - // 这里是包装成一个node - Node node = new Node(Thread.currentThread(), mode); - // Try the fast path of enq; backup to full enq on failure - // 最快的方式就是把当前线程的节点放在阻塞队列的最后 - Node pred = tail; - // 只有当tail,也就是pred不为空的时候可以直接接上 - if (pred != null) { - node.prev = pred; - // 如果这里cas成功了,就直接接上返回了 - if (compareAndSetTail(pred, node)) { - pred.next = node; - return node; - } - } - // 不然就会继续走到这里 - enq(node); - return node; - }然后就是enq的逻辑了
-
+// 代表当前持有独占锁的线程,举个最重要的使用例子,因为锁可以重入 +// reentrantLock.lock()可以嵌套调用多次,所以每次用这个来判断当前线程是否已经拥有了锁 +// if (currentThread == getExclusiveOwnerThread()) {state++} +private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer/** - * Inserts node into queue, initializing if necessary. See picture above. - * @param node the node to insert - * @return node's predecessor - */ - private Node enq(final Node node) { - for (;;) { - // 如果状态没变化的话,tail这时还是null的 - Node t = tail; - if (t == null) { // Must initialize - // 这里就会初始化头结点,就是个空节点 - if (compareAndSetHead(new Node())) - // tail也赋值成head - tail = head; - } else { - // 这里就设置tail了 - node.prev = t; - if (compareAndSetTail(t, node)) { - t.next = node; - return t; - } - } - } - }大概了解了 aqs 底层的双向等待队列,
+
结构是这样的![]()
每个 node 里面主要是的代码结构也比较简单static final class Node { + // 标识节点当前在共享模式下 + static final Node SHARED = new Node(); + // 标识节点当前在独占模式下 + static final Node EXCLUSIVE = null; -所以从这里可以看出来,其实head头结点不是个真实的带有线程的节点,并且不是在第一个线程进来的时候设置的
-解锁
通过代码来看下
-
- - - - -]]>/** - * Attempts to release this lock. - * - * <p>If the current thread is the holder of this lock then the hold - * count is decremented. If the hold count is now zero then the lock - * is released. If the current thread is not the holder of this - * lock then {@link IllegalMonitorStateException} is thrown. - * - * @throws IllegalMonitorStateException if the current thread does not - * hold this lock - */ - public void unlock() { - // 释放锁 - sync.release(1); - } -/** - * Releases in exclusive mode. Implemented by unblocking one or - * more threads if {@link #tryRelease} returns true. - * This method can be used to implement method {@link Lock#unlock}. - * - * @param arg the release argument. This value is conveyed to - * {@link #tryRelease} but is otherwise uninterpreted and - * can represent anything you like. - * @return the value returned from {@link #tryRelease} - */ - public final boolean release(int arg) { - // 尝试去释放 - if (tryRelease(arg)) { - Node h = head; - if (h != null && h.waitStatus != 0) - unparkSuccessor(h); - return true; - } - return false; - } -protected final boolean tryRelease(int releases) { - int c = getState() - releases; - if (Thread.currentThread() != getExclusiveOwnerThread()) - throw new IllegalMonitorStateException(); - boolean free = false; - // 判断是否完全释放锁,因为可重入 - if (c == 0) { - free = true; - setExclusiveOwnerThread(null); - } - setState(c); - return free; - } -// 这段代码和上面的一致,只是为了顺序性,又拷下来看下 - -public final boolean release(int arg) { - // 尝试去释放,如果是完全释放,返回的就是true,否则是false - if (tryRelease(arg)) { - Node h = head; - // 这里判断头结点是否为空以及waitStatus的状态,前面说了head节点其实是 - // 在第二个线程进来的时候初始化的,如果是空的话说明没后续节点,并且waitStatus - // 也表示了后续的等待状态 - if (h != null && h.waitStatus != 0) - unparkSuccessor(h); - return true; - } - return false; - } - -/** - * Wakes up node's successor, if one exists. - * - * @param node the node - */ -// 唤醒后继节点 - private void unparkSuccessor(Node node) { - /* - * If status is negative (i.e., possibly needing signal) try - * to clear in anticipation of signalling. It is OK if this - * fails or if status is changed by waiting thread. - */ - int ws = node.waitStatus; - if (ws < 0) - compareAndSetWaitStatus(node, ws, 0); - - /* - * Thread to unpark is held in successor, which is normally - * just the next node. But if cancelled or apparently null, - * traverse backwards from tail to find the actual - * non-cancelled successor. - */ - Node s = node.next; - // 如果后继节点是空或者当前节点取消等待了 - if (s == null || s.waitStatus > 0) { - s = null; - // 从后往前找,找到非取消的节点,注意这里不是找到就退出,而是一直找到头 - // 所以不必担心中间有取消的 - for (Node t = tail; t != null && t != node; t = t.prev) - if (t.waitStatus <= 0) - s = t; - } - if (s != null) - // 将其唤醒 - LockSupport.unpark(s.thread); - }- -Java -并发 -- -java -并发 -j.u.c -aqs -- AbstractQueuedSynchronizer -/2019/09/23/AbstractQueuedSynchronizer/ -最近看了大神的 AQS 的文章,之前总是断断续续地看一点,每次都知难而退,下次看又从头开始,昨天总算硬着头皮看完了第一部分
首先 AQS 只要有这些属性 -
-// 头结点,你直接把它当做 当前持有锁的线程 可能是最好理解的 -private transient volatile Node head; - -// 阻塞的尾节点,每个新的节点进来,都插入到最后,也就形成了一个链表 -private transient volatile Node tail; - -// 这个是最重要的,代表当前锁的状态,0代表没有被占用,大于 0 代表有线程持有当前锁 -// 这个值可以大于 1,是因为锁可以重入,每次重入都加上 1 -private volatile int state; - -// 代表当前持有独占锁的线程,举个最重要的使用例子,因为锁可以重入 -// reentrantLock.lock()可以嵌套调用多次,所以每次用这个来判断当前线程是否已经拥有了锁 -// if (currentThread == getExclusiveOwnerThread()) {state++} -private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer大概了解了 aqs 底层的双向等待队列,
-
结构是这样的![]()
每个 node 里面主要是的代码结构也比较简单static final class Node { - // 标识节点当前在共享模式下 - static final Node SHARED = new Node(); - // 标识节点当前在独占模式下 - static final Node EXCLUSIVE = null; - - // ======== 下面的几个int常量是给waitStatus用的 =========== - /** waitStatus value to indicate thread has cancelled */ - // 代码此线程取消了争抢这个锁 - static final int CANCELLED = 1; - /** waitStatus value to indicate successor's thread needs unparking */ - // 官方的描述是,其表示当前node的后继节点对应的线程需要被唤醒 - static final int SIGNAL = -1; - /** waitStatus value to indicate thread is waiting on condition */ - // 本文不分析condition,所以略过吧,下一篇文章会介绍这个 - static final int CONDITION = -2; - /** - * waitStatus value to indicate the next acquireShared should - * unconditionally propagate + // ======== 下面的几个int常量是给waitStatus用的 =========== + /** waitStatus value to indicate thread has cancelled */ + // 代码此线程取消了争抢这个锁 + static final int CANCELLED = 1; + /** waitStatus value to indicate successor's thread needs unparking */ + // 官方的描述是,其表示当前node的后继节点对应的线程需要被唤醒 + static final int SIGNAL = -1; + /** waitStatus value to indicate thread is waiting on condition */ + // 本文不分析condition,所以略过吧,下一篇文章会介绍这个 + static final int CONDITION = -2; + /** + * waitStatus value to indicate the next acquireShared should + * unconditionally propagate */ // 同样的不分析,略过吧 static final int PROPAGATE = -3; @@ -1074,90 +829,248 @@ public:- +Apollo 的 value 注解是怎么自动更新的 -/2020/11/01/Apollo-%E7%9A%84-value-%E6%B3%A8%E8%A7%A3%E6%98%AF%E6%80%8E%E4%B9%88%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0%E7%9A%84/ -在前司和目前公司,用的配置中心都是使用的 Apollo,经过了业界验证,比较强大的配置管理系统,特别是在0.10 后开始支持对使用 value 注解的配置值进行自动更新,今天刚好有个同学问到我,就顺便写篇文章记录下,其实也是借助于 spring 强大的 bean 生命周期管理,可以实现BeanPostProcessor接口,使用postProcessBeforeInitialization方法,来对bean 内部的属性和方法进行判断,是否有 value 注解,如果有就是将它注册到一个 map 中,可以看到这个方法 com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor#processField-
-@Override - protected void processField(Object bean, String beanName, Field field) { - // register @Value on field - Value value = field.getAnnotation(Value.class); - if (value == null) { - return; - } - Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value()); - - if (keys.isEmpty()) { - return; - } +AQS篇一 +/2021/02/14/AQS%E7%AF%87%E4%B8%80/ +很多东西都是时看时新,而且时间长了也会忘,所以再来复习下,也会有一些新的角度看法这次来聊下AQS的内容,主要是这几个点, + 第一个线程
第一个线程抢到锁了,此时state跟阻塞队列是怎么样的,其实这里是之前没理解对的地方
+
- for (String key : keys) { - SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false); - springValueRegistry.register(beanFactory, key, springValue); - logger.debug("Monitoring {}", springValue); - } - }/** + * Fair version of tryAcquire. Don't grant access unless + * recursive call or no waiters or is first. + */ + protected final boolean tryAcquire(int acquires) { + final Thread current = Thread.currentThread(); + int c = getState(); + // 这里如果state还是0说明锁还空着 + if (c == 0) { + // 因为是公平锁版本的,先去看下是否阻塞队列里有排着队的 + if (!hasQueuedPredecessors() && + compareAndSetState(0, acquires)) { + // 没有排队的,并且state使用cas设置成功的就标记当前占有锁的线程是我 + setExclusiveOwnerThread(current); + // 然后其实就返回了,包括阻塞队列的head和tail节点和waitStatus都没有设置 + return true; + } + } + else if (current == getExclusiveOwnerThread()) { + int nextc = c + acquires; + if (nextc < 0) + throw new Error("Maximum lock count exceeded"); + setState(nextc); + return true; + } + // 这里就是第二个线程会返回false + return false; + } + }然后我们看下这个
-springValueRegistry是啥玩意
-public class SpringValueRegistry { - private static final long CLEAN_INTERVAL_IN_SECONDS = 5; - private final Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap(); - private final AtomicBoolean initialized = new AtomicBoolean(false); - private final Object LOCK = new Object(); +第二个线程
当第二个线程进来的时候应该是怎么样,结合代码来看
+
- public void register(BeanFactory beanFactory, String key, SpringValue springValue) { - if (!registry.containsKey(beanFactory)) { - synchronized (LOCK) { - if (!registry.containsKey(beanFactory)) { - registry.put(beanFactory, LinkedListMultimap.<String, SpringValue>create()); +/** + * Acquires in exclusive mode, ignoring interrupts. Implemented + * by invoking at least once {@link #tryAcquire}, + * returning on success. Otherwise the thread is queued, possibly + * repeatedly blocking and unblocking, invoking {@link + * #tryAcquire} until success. This method can be used + * to implement method {@link Lock#lock}. + * + * @param arg the acquire argument. This value is conveyed to + * {@link #tryAcquire} but is otherwise uninterpreted and + * can represent anything you like. + */ + public final void acquire(int arg) { + // 前面第一种情况是tryAcquire直接成功了,这个if判断第一个条件就是false,就不往下执行了 + // 如果是第二个线程,第一个条件获取锁不成功,条件判断!tryAcquire(arg) == true,就会走 + // acquireQueued(addWaiter(Node.EXCLUSIVE), arg) + if (!tryAcquire(arg) && + acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) + selfInterrupt(); + }然后来看下addWaiter的逻辑
+
- // lazy initialize - if (initialized.compareAndSet(false, true)) { - initialize(); +/** + * Creates and enqueues node for current thread and given mode. + * + * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared + * @return the new node + */ + private Node addWaiter(Node mode) { + // 这里是包装成一个node + Node node = new Node(Thread.currentThread(), mode); + // Try the fast path of enq; backup to full enq on failure + // 最快的方式就是把当前线程的节点放在阻塞队列的最后 + Node pred = tail; + // 只有当tail,也就是pred不为空的时候可以直接接上 + if (pred != null) { + node.prev = pred; + // 如果这里cas成功了,就直接接上返回了 + if (compareAndSetTail(pred, node)) { + pred.next = node; + return node; + } } - } - } - - registry.get(beanFactory).put(key, springValue); + // 不然就会继续走到这里 + enq(node); + return node; + }然后就是enq的逻辑了
+
+ +/** + * Inserts node into queue, initializing if necessary. See picture above. + * @param node the node to insert + * @return node's predecessor + */ + private Node enq(final Node node) { + for (;;) { + // 如果状态没变化的话,tail这时还是null的 + Node t = tail; + if (t == null) { // Must initialize + // 这里就会初始化头结点,就是个空节点 + if (compareAndSetHead(new Node())) + // tail也赋值成head + tail = head; + } else { + // 这里就设置tail了 + node.prev = t; + if (compareAndSetTail(t, node)) { + t.next = node; + return t; + } + } + } + }所以从这里可以看出来,其实head头结点不是个真实的带有线程的节点,并且不是在第一个线程进来的时候设置的
+解锁
通过代码来看下
+
-/** + * Attempts to release this lock. + * + * <p>If the current thread is the holder of this lock then the hold + * count is decremented. If the hold count is now zero then the lock + * is released. If the current thread is not the holder of this + * lock then {@link IllegalMonitorStateException} is thrown. + * + * @throws IllegalMonitorStateException if the current thread does not + * hold this lock + */ + public void unlock() { + // 释放锁 + sync.release(1); } - }这类其实就是个 map 来存放 springvalue,然后有
-com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener来监听更新操作,当有变更时
+ + - // 2. check whether the value is really changed or not (since spring property sources have hierarchies) - // 这里其实有一点比较绕,是因为 Apollo 里的 namespace 划分,会出现 key 相同,但是 namespace 不同的情况,所以会有个优先级存在,所以需要去校验 environment 里面的是否已经更新,如果未更新则表示不需要更新 - if (!shouldTriggerAutoUpdate(changeEvent, key)) { - continue; - } - // 3. update the value - for (SpringValue val : targetValues) { - updateSpringValue(val); - } - } - }@Override - public void onChange(ConfigChangeEvent changeEvent) { - Set<String> keys = changeEvent.changedKeys(); - if (CollectionUtils.isEmpty(keys)) { - return; - } - for (String key : keys) { - // 1. check whether the changed key is relevant - Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key); - if (targetValues == null || targetValues.isEmpty()) { - continue; - } +/** + * Releases in exclusive mode. Implemented by unblocking one or + * more threads if {@link #tryRelease} returns true. + * This method can be used to implement method {@link Lock#unlock}. + * + * @param arg the release argument. This value is conveyed to + * {@link #tryRelease} but is otherwise uninterpreted and + * can represent anything you like. + * @return the value returned from {@link #tryRelease} + */ + public final boolean release(int arg) { + // 尝试去释放 + if (tryRelease(arg)) { + Node h = head; + if (h != null && h.waitStatus != 0) + unparkSuccessor(h); + return true; + } + return false; + } +protected final boolean tryRelease(int releases) { + int c = getState() - releases; + if (Thread.currentThread() != getExclusiveOwnerThread()) + throw new IllegalMonitorStateException(); + boolean free = false; + // 判断是否完全释放锁,因为可重入 + if (c == 0) { + free = true; + setExclusiveOwnerThread(null); + } + setState(c); + return free; + } +// 这段代码和上面的一致,只是为了顺序性,又拷下来看下 + +public final boolean release(int arg) { + // 尝试去释放,如果是完全释放,返回的就是true,否则是false + if (tryRelease(arg)) { + Node h = head; + // 这里判断头结点是否为空以及waitStatus的状态,前面说了head节点其实是 + // 在第二个线程进来的时候初始化的,如果是空的话说明没后续节点,并且waitStatus + // 也表示了后续的等待状态 + if (h != null && h.waitStatus != 0) + unparkSuccessor(h); + return true; + } + return false; + } + +/** + * Wakes up node's successor, if one exists. + * + * @param node the node + */ +// 唤醒后继节点 + private void unparkSuccessor(Node node) { + /* + * If status is negative (i.e., possibly needing signal) try + * to clear in anticipation of signalling. It is OK if this + * fails or if status is changed by waiting thread. + */ + int ws = node.waitStatus; + if (ws < 0) + compareAndSetWaitStatus(node, ws, 0); + + /* + * Thread to unpark is held in successor, which is normally + * just the next node. But if cancelled or apparently null, + * traverse backwards from tail to find the actual + * non-cancelled successor. + */ + Node s = node.next; + // 如果后继节点是空或者当前节点取消等待了 + if (s == null || s.waitStatus > 0) { + s = null; + // 从后往前找,找到非取消的节点,注意这里不是找到就退出,而是一直找到头 + // 所以不必担心中间有取消的 + for (Node t = tail; t != null && t != node; t = t.prev) + if (t.waitStatus <= 0) + s = t; + } + if (s != null) + // 将其唤醒 + LockSupport.unpark(s.thread); + }其实原理很简单,就是得了解知道下
]]>Java -Apollo -value +并发 - +Java -Apollo -value -注解 -environment +java +并发 +j.u.c +aqs ++ 2021 年终总结 +/2022/01/22/2021-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ +又是一年年终总结,本着极度讨厌实时需求的理念,我还是 T+N 发布这个年终总结 + +工作篇
工作没什么大变化,有了些微的提升,可能因为是来了之后做了些项目对比公司与来还算是比较重要的,但是技术难度上没有特别突出的点,可能最开始用 openresty+lua 做了个 ab 测的工具,还是让我比较满意的,后面一般都是业务型的需求,今年可能在业务相关的技术逻辑上有了一些深度的了解,而原来一直想做的业务架构升级和通用型技术中间件这样的优化还是停留在想象中,前面说的 ab 测应该算是个半成品,还是没能多走出这一步,得需要多做一些实在的事情,比如轻量级的业务框架,能够对原先不熟悉的业务逻辑,代码逻辑有比较深入的理解,而不是一直都是让特定的同学负责特定的逻辑,很多时候还是在偷懒,习惯以一些简单安全的方案去做事情,在技术上还是要有所追求,还有就是能够在新语言,主要是 rust,swift 这类的能有些小玩具可以做,rust 的话是因为今年看了一本相关的书,后面三分之一其实消化得不好,这本书整体来说是很不错的,只是 rust 本身在所有权这块,还有引用包装等方面是设计得比较难懂,也可能是我基础差,所以还是想在复习下,可以做一个简单的命令行工具这种,然后 swift 是想说可以做点 mac 的小软件,原生的毕竟性能好点,又小。基于 web 做的客户端大部分都是又丑又大,极少数能好看点,但也是很重,起码 7~80M 的大小,原生的估计能除以 10。
+]]>
整体的职业规划貌似陷入了比较大的困惑期,在目前公司发展前景不是很大,但是出去貌似也没有比较适合我的机会,总的来说还是杭州比较卷,个人觉得有自己的时间是非常重要的,而且这个不光是用来自我提升的,还是让自己有足够的时间做缓冲,有足够的时间锻炼减肥,时间少的情况下,不光会在仅有的时间里暴饮暴食,还没空锻炼,身体是革命的本钱,现在其实能特别明显地感觉到身体状态下滑,容易疲劳,焦虑。所以是否也许有可能以后要往外企这类的方向去发展。
工作上其实还是有个不大不小的缺点,就是容易激动,容易焦虑,前一点可能有稍稍地改观,因为工作中的很多现状其实是我个人难以改变的,即使觉得不合理,但是结构在那里,还不如自己放宽心,尽量做好事情就行。第二点的话还是做得比较差,一直以来抗压能力都比较差,跟成长环境,家庭环境都有比较大的关系,而且说实在的特别是父母,基本也没有在这方面给我正向的帮助,比较擅长给我施压,从小就是通过压力让我好好读书,当个乖学生,考个好学校,并没有能真正地理解我的压力,教我或者帮助我解压,只会在那说着不着边际的空话,甚至经常反过来对我施压。还是希望能慢慢解开,这点可能对我身体也有影响,也许需要看一些心理疏导相关的书籍。工作篇暂时到这,后续还有其他篇,未完待续哈哈😀+ +生活 +年终总结 ++ 生活 +年终总结 +2021 +拖更 @@ -1214,57 +1127,144 @@ Node *clone(Node *graph) { - +Comparator使用小记 -/2020/04/05/Comparator%E4%BD%BF%E7%94%A8%E5%B0%8F%E8%AE%B0/ -在Java8的stream之前,将对象进行排序的时候,可能需要对象实现Comparable接口,或者自己实现一个Comparator, - +比如这样子
-我的对象是Entity
-
+public class Entity { - - private Long id; - - private Long sortValue; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; +Apollo 的 value 注解是怎么自动更新的 +/2020/11/01/Apollo-%E7%9A%84-value-%E6%B3%A8%E8%A7%A3%E6%98%AF%E6%80%8E%E4%B9%88%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0%E7%9A%84/ +在前司和目前公司,用的配置中心都是使用的 Apollo,经过了业界验证,比较强大的配置管理系统,特别是在0.10 后开始支持对使用 value 注解的配置值进行自动更新,今天刚好有个同学问到我,就顺便写篇文章记录下,其实也是借助于 spring 强大的 bean 生命周期管理,可以实现BeanPostProcessor接口,使用postProcessBeforeInitialization方法,来对bean 内部的属性和方法进行判断,是否有 value 注解,如果有就是将它注册到一个 map 中,可以看到这个方法 com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor#processField+
+ }@Override + protected void processField(Object bean, String beanName, Field field) { + // register @Value on field + Value value = field.getAnnotation(Value.class); + if (value == null) { + return; } + Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value()); - public Long getSortValue() { - return sortValue; + if (keys.isEmpty()) { + return; } - public void setSortValue(Long sortValue) { - this.sortValue = sortValue; + for (String key : keys) { + SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false); + springValueRegistry.register(beanFactory, key, springValue); + logger.debug("Monitoring {}", springValue); } -}然后我们看下这个
+springValueRegistry是啥玩意public class SpringValueRegistry { + private static final long CLEAN_INTERVAL_IN_SECONDS = 5; + private final Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap(); + private final AtomicBoolean initialized = new AtomicBoolean(false); + private final Object LOCK = new Object(); -Comparator
-
-public class MyComparator implements Comparator { - @Override - public int compare(Object o1, Object o2) { - Entity e1 = (Entity) o1; - Entity e2 = (Entity) o2; - if (e1.getSortValue() < e2.getSortValue()) { - return -1; - } else if (e1.getSortValue().equals(e2.getSortValue())) { - return 0; - } else { - return 1; + public void register(BeanFactory beanFactory, String key, SpringValue springValue) { + if (!registry.containsKey(beanFactory)) { + synchronized (LOCK) { + if (!registry.containsKey(beanFactory)) { + registry.put(beanFactory, LinkedListMultimap.<String, SpringValue>create()); } + } } -}比较代码
-
+private static MyComparator myComparator = new MyComparator(); + registry.get(beanFactory).put(key, springValue); - public static void main(String[] args) { - List<Entity> list = new ArrayList<Entity>(); - Entity e1 = new Entity(); - e1.setId(1L); + // lazy initialize + if (initialized.compareAndSet(false, true)) { + initialize(); + } + }这类其实就是个 map 来存放 springvalue,然后有
+com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener来监听更新操作,当有变更时
+@Override + public void onChange(ConfigChangeEvent changeEvent) { + Set<String> keys = changeEvent.changedKeys(); + if (CollectionUtils.isEmpty(keys)) { + return; + } + for (String key : keys) { + // 1. check whether the changed key is relevant + Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key); + if (targetValues == null || targetValues.isEmpty()) { + continue; + } + + // 2. check whether the value is really changed or not (since spring property sources have hierarchies) + // 这里其实有一点比较绕,是因为 Apollo 里的 namespace 划分,会出现 key 相同,但是 namespace 不同的情况,所以会有个优先级存在,所以需要去校验 environment 里面的是否已经更新,如果未更新则表示不需要更新 + if (!shouldTriggerAutoUpdate(changeEvent, key)) { + continue; + } + + // 3. update the value + for (SpringValue val : targetValues) { + updateSpringValue(val); + } + } + }其实原理很简单,就是得了解知道下
+]]>+ +Java +Apollo +value ++ +Java +Apollo +value +注解 +environment ++ +Comparator使用小记 +/2020/04/05/Comparator%E4%BD%BF%E7%94%A8%E5%B0%8F%E8%AE%B0/ +在Java8的stream之前,将对象进行排序的时候,可能需要对象实现Comparable接口,或者自己实现一个Comparator, + 比如这样子
+我的对象是Entity
+
+ +public class Entity { + + private Long id; + + private Long sortValue; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getSortValue() { + return sortValue; + } + + public void setSortValue(Long sortValue) { + this.sortValue = sortValue; + } +}Comparator
+
+ +public class MyComparator implements Comparator { + @Override + public int compare(Object o1, Object o2) { + Entity e1 = (Entity) o1; + Entity e2 = (Entity) o2; + if (e1.getSortValue() < e2.getSortValue()) { + return -1; + } else if (e1.getSortValue().equals(e2.getSortValue())) { + return 0; + } else { + return 1; + } + } +}比较代码
+private static MyComparator myComparator = new MyComparator(); + + public static void main(String[] args) { + List<Entity> list = new ArrayList<Entity>(); + Entity e1 = new Entity(); + e1.setId(1L); e1.setSortValue(1L); list.add(e1); Entity e2 = new Entity(); @@ -1314,6 +1314,134 @@ Node *clone(Node *graph) {nullsfirst + +Disruptor 系列一 +/2022/02/13/Disruptor-%E7%B3%BB%E5%88%97%E4%B8%80/ +很久之前就听说过这个框架,不过之前有点跟消息队列混起来,这个也是种队列,但不是跟 rocketmq,nsq 那种一样的,而是在进程内部提供队列服务的,偏向于取代 +ArrayBlockingQueue,因为这个阻塞队列是使用了锁来控制阻塞,关于并发其实有一些通用的最佳实践,就是用锁,即使是 JDK 提供的锁,也是比较耗资源的,当然这是跟不加锁的对比,同样是锁,JDK 的实现还是性能比较优秀的。常见的阻塞队列中例如ArrayBlockingQueue和LinkedBlockingQueue都有锁的身影的存在,区别在于ArrayBlockingQueue是一把锁,后者是两把锁,不过重点不在几把锁,这里其实是两个问题,一个是所谓的lock free, 对于一个单生产者的disruptor来说,因为写入是只有一个线程的,是可以不用加锁,多生产者的时候使用的是 cas 来获取对应的写入坑位,另一个是解决“伪共享”问题,后面可以详细点分析,先介绍下使用
首先是数据源 +
+public class LongEvent { + private long value; + + public void set(long value) { + this.value = value; + } + + public long getValue() { + return value; + } + + public void setValue(long value) { + this.value = value; + } +}事件生产
+
+public class LongEventFactory implements EventFactory<LongEvent> +{ + public LongEvent newInstance() + { + return new LongEvent(); + } +}事件处理器
+
+public class LongEventHandler implements EventHandler<LongEvent> { + + // event 事件, + // sequence 当前的序列 + // 是否当前批次最后一个数据 + public void onEvent(LongEvent event, long sequence, boolean endOfBatch) + { + String str = String.format("long event : %s l:%s b:%s", event.getValue(), sequence, endOfBatch); + System.out.println(str); + } +} +主方法代码
+
+package disruptor; + +import com.lmax.disruptor.RingBuffer; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.util.DaemonThreadFactory; + +import java.nio.ByteBuffer; + +public class LongEventMain +{ + public static void main(String[] args) throws Exception + { + // 这个需要是 2 的幂次,这样在定位的时候只需要位移操作,也能减少各种计算操作 + int bufferSize = 1024; + + Disruptor<LongEvent> disruptor = + new Disruptor<>(LongEvent::new, bufferSize, DaemonThreadFactory.INSTANCE); + + // 类似于注册处理器 + disruptor.handleEventsWith(new LongEventHandler()); + // 或者直接用 lambda + disruptor.handleEventsWith((event, sequence, endOfBatch) -> + System.out.println("Event: " + event)); + // 启动我们的 disruptor + disruptor.start(); + + + RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer(); + ByteBuffer bb = ByteBuffer.allocate(8); + for (long l = 0; true; l++) + { + bb.putLong(0, l); + // 生产事件 + ringBuffer.publishEvent((event, sequence, buffer) -> event.set(buffer.getLong(0)), bb); + Thread.sleep(1000); + } + } +}运行下可以看到运行结果
+]]>![]()
这里其实就只是最简单的使用,生产者只有一个,然后也不是批量的。+ +Java ++ +Java +Disruptor ++ Disruptor 系列二 +/2022/02/27/Disruptor-%E7%B3%BB%E5%88%97%E4%BA%8C/ +这里开始慢慢深入的讲一下 disruptor,首先是 +lock free, 相比于前面介绍的两个阻塞队列,
disruptor 本身是不直接使用锁的,因为本身的设计是单个线程去生产,通过 cas 来维护头指针,
不直接维护尾指针,这样就减少了锁的使用,提升了性能;第二个是这次介绍的重点,
减少false sharing的情况,也就是常说的 伪共享 问题,那么什么叫 伪共享 呢,
这里要扯到一些 cpu 缓存的知识,![]()
譬如我在用的这个笔记本![]()
这里就可能看到 L2 Cache 就是针对每个核的![]()
这里可以看到现代 CPU 的结构里,分为三级缓存,越靠近 cpu 的速度越快,存储容量越小,
而 L1 跟 L2 是 CPU 核专属的每个核都有自己的 L1 和 L2 的,其中 L1 还分为数据和指令,
像我上面的图中显示的 L1 Cache 只有 64KB 大小,其中数据 32KB,指令 32KB,
而 L2 则有 256KB,L3 有 4MB,其中的 Line Size 是我们这里比较重要的一个值,
CPU 其实会就近地从 Cache 中读取数据,碰到Cache Miss就再往下一级 Cache 读取,
每次读取是按照缓存行Cache Line读取,并且也遵循了“就近原则”,
也就是相近的数据有可能也会马上被读取,所以以行的形式读取,然而这也造成了false sharing,
因为类似于ArrayBlockingQueue,需要有takeIndex,putIndex,count, 因为在同一个类中,
很有可能存在于同一个Cache Line中,但是这几个值会被不同的线程修改,
导致从 Cache 取出来以后立马就会被失效,所谓的就近原则也就没用了,
因为需要反复地标记 dirty 脏位,然后把 Cache 刷掉,就造成了false sharing这种情况
而在disruptor中则使用了填充的方式,让我的头指针能够不产生false sharing+
+class LhsPadding +{ + protected long p1, p2, p3, p4, p5, p6, p7; +} + +class Value extends LhsPadding +{ + protected volatile long value; +} + +class RhsPadding extends Value +{ + protected long p9, p10, p11, p12, p13, p14, p15; +} + +/** + * <p>Concurrent sequence class used for tracking the progress of + * the ring buffer and event processors. Support a number + * of concurrent operations including CAS and order writes. + * + * <p>Also attempts to be more efficient with regards to false + * sharing by adding padding around the volatile field. + */ +public class Sequence extends RhsPadding +{通过代码可以看到,sequence 中其实真正有意义的是 value 字段,因为需要在多线程环境下可见也
+]]>
使用了volatile关键字,而LhsPadding和RhsPadding分别在value 前后填充了各
7 个long型的变量,long型的变量在 Java 中是占用 8 bytes,这样就相当于不管怎么样,
value 都会单独使用一个缓存行,使得其不会产生false sharing的问题。+ +Java ++ +Java +Disruptor +Filter, Interceptor, Aop, 啥, 啥, 啥? 这些都是啥? /2020/08/22/Filter-Intercepter-Aop-%E5%95%A5-%E5%95%A5-%E5%95%A5-%E8%BF%99%E4%BA%9B%E9%83%BD%E6%98%AF%E5%95%A5/ @@ -1865,226 +1993,26 @@ Node *clone(Node *graph) {- -Disruptor 系列二 -/2022/02/27/Disruptor-%E7%B3%BB%E5%88%97%E4%BA%8C/ -这里开始慢慢深入的讲一下 disruptor,首先是 lock free, 相比于前面介绍的两个阻塞队列,
disruptor 本身是不直接使用锁的,因为本身的设计是单个线程去生产,通过 cas 来维护头指针,
不直接维护尾指针,这样就减少了锁的使用,提升了性能;第二个是这次介绍的重点,
减少false sharing的情况,也就是常说的 伪共享 问题,那么什么叫 伪共享 呢,
这里要扯到一些 cpu 缓存的知识,![]()
譬如我在用的这个笔记本![]()
这里就可能看到 L2 Cache 就是针对每个核的![]()
这里可以看到现代 CPU 的结构里,分为三级缓存,越靠近 cpu 的速度越快,存储容量越小,
而 L1 跟 L2 是 CPU 核专属的每个核都有自己的 L1 和 L2 的,其中 L1 还分为数据和指令,
像我上面的图中显示的 L1 Cache 只有 64KB 大小,其中数据 32KB,指令 32KB,
而 L2 则有 256KB,L3 有 4MB,其中的 Line Size 是我们这里比较重要的一个值,
CPU 其实会就近地从 Cache 中读取数据,碰到Cache Miss就再往下一级 Cache 读取,
每次读取是按照缓存行Cache Line读取,并且也遵循了“就近原则”,
也就是相近的数据有可能也会马上被读取,所以以行的形式读取,然而这也造成了false sharing,
因为类似于ArrayBlockingQueue,需要有takeIndex,putIndex,count, 因为在同一个类中,
很有可能存在于同一个Cache Line中,但是这几个值会被不同的线程修改,
导致从 Cache 取出来以后立马就会被失效,所谓的就近原则也就没用了,
因为需要反复地标记 dirty 脏位,然后把 Cache 刷掉,就造成了false sharing这种情况
而在disruptor中则使用了填充的方式,让我的头指针能够不产生false sharing-class LhsPadding -{ - protected long p1, p2, p3, p4, p5, p6, p7; -} +JVM源码分析之G1垃圾收集器分析一 +/2019/12/07/JVM-G1-Part-1/ +对 Java 的 gc 实现比较感兴趣,原先一般都是看周志明的书,但其实并没有讲具体的 gc 源码,而是把整个思路和流程讲解了一下 -
特别是 G1 的具体实现
一般对 G1 的理解其实就是把原先整块的新生代老年代分成了以 region 为单位的小块内存,简而言之,就是原先对新生代老年代的收集会涉及到整个代的堆内存空间,而G1 把它变成了更细致的小块内存
这带来了一个很明显的好处和一个很明显的坏处,好处是内存收集可以更灵活,耗时会变短,但整个收集的处理复杂度就变高了
目前看了一点点关于 G1 收集的预期时间相关的代码 +
-HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size, + uint gc_count_before, + bool* succeeded, + GCCause::Cause gc_cause) { + assert_heap_not_locked_and_not_at_safepoint(); + VM_G1CollectForAllocation op(word_size, + gc_count_before, + gc_cause, + false, /* should_initiate_conc_mark */ + g1_policy()->max_pause_time_ms()); + VMThread::execute(&op); -class Value extends LhsPadding -{ - protected volatile long value; -} - -class RhsPadding extends Value -{ - protected long p9, p10, p11, p12, p13, p14, p15; -} - -/** - * <p>Concurrent sequence class used for tracking the progress of - * the ring buffer and event processors. Support a number - * of concurrent operations including CAS and order writes. - * - * <p>Also attempts to be more efficient with regards to false - * sharing by adding padding around the volatile field. - */ -public class Sequence extends RhsPadding -{通过代码可以看到,sequence 中其实真正有意义的是 value 字段,因为需要在多线程环境下可见也
-]]>
使用了volatile关键字,而LhsPadding和RhsPadding分别在value 前后填充了各
7 个long型的变量,long型的变量在 Java 中是占用 8 bytes,这样就相当于不管怎么样,
value 都会单独使用一个缓存行,使得其不会产生false sharing的问题。- -Java -- -Java -Disruptor -- -Disruptor 系列一 -/2022/02/13/Disruptor-%E7%B3%BB%E5%88%97%E4%B8%80/ -很久之前就听说过这个框架,不过之前有点跟消息队列混起来,这个也是种队列,但不是跟 rocketmq,nsq 那种一样的,而是在进程内部提供队列服务的,偏向于取代 -ArrayBlockingQueue,因为这个阻塞队列是使用了锁来控制阻塞,关于并发其实有一些通用的最佳实践,就是用锁,即使是 JDK 提供的锁,也是比较耗资源的,当然这是跟不加锁的对比,同样是锁,JDK 的实现还是性能比较优秀的。常见的阻塞队列中例如ArrayBlockingQueue和LinkedBlockingQueue都有锁的身影的存在,区别在于ArrayBlockingQueue是一把锁,后者是两把锁,不过重点不在几把锁,这里其实是两个问题,一个是所谓的lock free, 对于一个单生产者的disruptor来说,因为写入是只有一个线程的,是可以不用加锁,多生产者的时候使用的是 cas 来获取对应的写入坑位,另一个是解决“伪共享”问题,后面可以详细点分析,先介绍下使用
首先是数据源 -
-public class LongEvent { - private long value; - - public void set(long value) { - this.value = value; - } - - public long getValue() { - return value; - } - - public void setValue(long value) { - this.value = value; - } -}事件生产
-
-public class LongEventFactory implements EventFactory<LongEvent> -{ - public LongEvent newInstance() - { - return new LongEvent(); - } -}事件处理器
-
-public class LongEventHandler implements EventHandler<LongEvent> { - - // event 事件, - // sequence 当前的序列 - // 是否当前批次最后一个数据 - public void onEvent(LongEvent event, long sequence, boolean endOfBatch) - { - String str = String.format("long event : %s l:%s b:%s", event.getValue(), sequence, endOfBatch); - System.out.println(str); - } -} -主方法代码
-
-package disruptor; - -import com.lmax.disruptor.RingBuffer; -import com.lmax.disruptor.dsl.Disruptor; -import com.lmax.disruptor.util.DaemonThreadFactory; - -import java.nio.ByteBuffer; - -public class LongEventMain -{ - public static void main(String[] args) throws Exception - { - // 这个需要是 2 的幂次,这样在定位的时候只需要位移操作,也能减少各种计算操作 - int bufferSize = 1024; - - Disruptor<LongEvent> disruptor = - new Disruptor<>(LongEvent::new, bufferSize, DaemonThreadFactory.INSTANCE); - - // 类似于注册处理器 - disruptor.handleEventsWith(new LongEventHandler()); - // 或者直接用 lambda - disruptor.handleEventsWith((event, sequence, endOfBatch) -> - System.out.println("Event: " + event)); - // 启动我们的 disruptor - disruptor.start(); - - - RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer(); - ByteBuffer bb = ByteBuffer.allocate(8); - for (long l = 0; true; l++) - { - bb.putLong(0, l); - // 生产事件 - ringBuffer.publishEvent((event, sequence, buffer) -> event.set(buffer.getLong(0)), bb); - Thread.sleep(1000); - } - } -}运行下可以看到运行结果
-]]>![]()
这里其实就只是最简单的使用,生产者只有一个,然后也不是批量的。- -Java -- -Java -Disruptor -- -Leetcode 021 合并两个有序链表 ( Merge Two Sorted Lists ) 题解分析 -/2021/10/07/Leetcode-021-%E5%90%88%E5%B9%B6%E4%B8%A4%E4%B8%AA%E6%9C%89%E5%BA%8F%E9%93%BE%E8%A1%A8-Merge-Two-Sorted-Lists-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/ -题目介绍 -Merge two sorted linked lists and return it as a sorted list. The list should be made by splicing together the nodes of the first two lists.
-将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
-示例 1
-![]()
-
-输入:l1 = [1,2,4], l2 = [1,3,4]
-
输出:[1,1,2,3,4,4]示例 2
-
-输入: l1 = [], l2 = []
-
输出: []示例 3
-
-输入: l1 = [], l2 = [0]
-
输出: [0]简要分析
这题是 Easy 的,看着也挺简单,两个链表进行合并,就是比较下大小,可能将就点的话最好就在两个链表中原地合并
-题解代码
- -public ListNode mergeTwoLists(ListNode l1, ListNode l2) { - // 下面两个if判断了入参的边界,如果其一为null,直接返回另一个就可以了 - if (l1 == null) { - return l2; - } - if (l2 == null) { - return l1; - } - // new 一个合并后的头结点 - ListNode merged = new ListNode(); - // 这个是当前节点 - ListNode current = merged; - // 一开始给这个while加了l1和l2不全为null的条件,后面想了下不需要 - // 因为内部前两个if就是跳出条件 - while (true) { - if (l1 == null) { - // 这里其实跟开头类似,只不过这里需要将l2剩余部分接到merged链表后面 - // 所以不能是直接current = l2,这样就是把后面的直接丢了 - current.val = l2.val; - current.next = l2.next; - break; - } - if (l2 == null) { - current.val = l1.val; - current.next = l1.next; - break; - } - // 这里是两个链表都不为空的时候,就比较下大小 - if (l1.val < l2.val) { - current.val = l1.val; - l1 = l1.next; - } else { - current.val = l2.val; - l2 = l2.next; - } - // 这里是new个新的,其实也可以放在循环头上 - current.next = new ListNode(); - current = current.next; - } - current = null; - // 返回这个头结点 - return merged; - }结果
-]]>![]()
- -Java -leetcode -- -leetcode -java -题解 -- JVM源码分析之G1垃圾收集器分析一 -/2019/12/07/JVM-G1-Part-1/ -对 Java 的 gc 实现比较感兴趣,原先一般都是看周志明的书,但其实并没有讲具体的 gc 源码,而是把整个思路和流程讲解了一下
特别是 G1 的具体实现
一般对 G1 的理解其实就是把原先整块的新生代老年代分成了以 region 为单位的小块内存,简而言之,就是原先对新生代老年代的收集会涉及到整个代的堆内存空间,而G1 把它变成了更细致的小块内存
这带来了一个很明显的好处和一个很明显的坏处,好处是内存收集可以更灵活,耗时会变短,但整个收集的处理复杂度就变高了
目前看了一点点关于 G1 收集的预期时间相关的代码 -HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size, - uint gc_count_before, - bool* succeeded, - GCCause::Cause gc_cause) { - assert_heap_not_locked_and_not_at_safepoint(); - VM_G1CollectForAllocation op(word_size, - gc_count_before, - gc_cause, - false, /* should_initiate_conc_mark */ - g1_policy()->max_pause_time_ms()); - VMThread::execute(&op); - - HeapWord* result = op.result(); - bool ret_succeeded = op.prologue_succeeded() && op.pause_succeeded(); - assert(result == NULL || ret_succeeded, - "the result should be NULL if the VM did not succeed"); - *succeeded = ret_succeeded; + HeapWord* result = op.result(); + bool ret_succeeded = op.prologue_succeeded() && op.pause_succeeded(); + assert(result == NULL || ret_succeeded, + "the result should be NULL if the VM did not succeed"); + *succeeded = ret_succeeded; assert_heap_not_locked(); return result; @@ -2671,35 +2599,107 @@ Node *clone(Node *graph) {- +Leetcode 028 实现 strStr() ( Implement strStr() ) 题解分析 -/2021/10/31/Leetcode-028-%E5%AE%9E%E7%8E%B0-strStr-Implement-strStr-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/ -题目介绍 Implement
-strStr().Return the index of the first occurrence of needle in haystack, or
--1ifneedleis not part ofhaystack.Clarification:
What should we return when
-needleis an empty string? This is a great question to ask during an interview.For the purpose of this problem, we will return 0 when
-needleis an empty string. This is consistent to C’sstrstr()and Java’sindexOf().示例
Example 1:
-
-Input: haystack = "hello", needle = "ll" -Output: 2Example 2:
-
-Input: haystack = "aaaaa", needle = "bba" -Output: -1Example 3:
-
- -Input: haystack = "", needle = "" -Output: 0题解
字符串比较其实是写代码里永恒的主题,底层的编译器等处理肯定需要字符串对比,像 kmp 算法也是很厉害
-code
public int strStr(String haystack, String needle) { - // 如果两个字符串都为空,返回 -1 - if (haystack == null || needle == null) { - return -1; +Leetcode 021 合并两个有序链表 ( Merge Two Sorted Lists ) 题解分析 +/2021/10/07/Leetcode-021-%E5%90%88%E5%B9%B6%E4%B8%A4%E4%B8%AA%E6%9C%89%E5%BA%8F%E9%93%BE%E8%A1%A8-Merge-Two-Sorted-Lists-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/ +题目介绍 +Merge two sorted linked lists and return it as a sorted list. The list should be made by splicing together the nodes of the first two lists.
+将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
+示例 1
+![]()
+
+输入:l1 = [1,2,4], l2 = [1,3,4]
+
输出:[1,1,2,3,4,4]示例 2
+
+输入: l1 = [], l2 = []
+
输出: []示例 3
+
+输入: l1 = [], l2 = [0]
+
输出: [0]简要分析
这题是 Easy 的,看着也挺简单,两个链表进行合并,就是比较下大小,可能将就点的话最好就在两个链表中原地合并
+题解代码
+ +public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + // 下面两个if判断了入参的边界,如果其一为null,直接返回另一个就可以了 + if (l1 == null) { + return l2; } - // 如果 haystack 长度小于 needle 长度,返回 -1 - if (haystack.length() < needle.length()) { - return -1; + if (l2 == null) { + return l1; } - // 如果 needle 为空字符串,返回 0 - if (needle.equals("")) { - return 0; + // new 一个合并后的头结点 + ListNode merged = new ListNode(); + // 这个是当前节点 + ListNode current = merged; + // 一开始给这个while加了l1和l2不全为null的条件,后面想了下不需要 + // 因为内部前两个if就是跳出条件 + while (true) { + if (l1 == null) { + // 这里其实跟开头类似,只不过这里需要将l2剩余部分接到merged链表后面 + // 所以不能是直接current = l2,这样就是把后面的直接丢了 + current.val = l2.val; + current.next = l2.next; + break; + } + if (l2 == null) { + current.val = l1.val; + current.next = l1.next; + break; + } + // 这里是两个链表都不为空的时候,就比较下大小 + if (l1.val < l2.val) { + current.val = l1.val; + l1 = l1.next; + } else { + current.val = l2.val; + l2 = l2.next; + } + // 这里是new个新的,其实也可以放在循环头上 + current.next = new ListNode(); + current = current.next; + } + current = null; + // 返回这个头结点 + return merged; + }结果
+]]>![]()
+ +Java +leetcode ++ +leetcode +java +题解 ++ -Leetcode 028 实现 strStr() ( Implement strStr() ) 题解分析 +/2021/10/31/Leetcode-028-%E5%AE%9E%E7%8E%B0-strStr-Implement-strStr-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/ +题目介绍 Implement
+strStr().Return the index of the first occurrence of needle in haystack, or
+-1ifneedleis not part ofhaystack.Clarification:
What should we return when
+needleis an empty string? This is a great question to ask during an interview.For the purpose of this problem, we will return 0 when
+needleis an empty string. This is consistent to C’sstrstr()and Java’sindexOf().示例
Example 1:
+
+Input: haystack = "hello", needle = "ll" +Output: 2Example 2:
+
+Input: haystack = "aaaaa", needle = "bba" +Output: -1Example 3:
+
+ +Input: haystack = "", needle = "" +Output: 0题解
字符串比较其实是写代码里永恒的主题,底层的编译器等处理肯定需要字符串对比,像 kmp 算法也是很厉害
+code
public int strStr(String haystack, String needle) { + // 如果两个字符串都为空,返回 -1 + if (haystack == null || needle == null) { + return -1; + } + // 如果 haystack 长度小于 needle 长度,返回 -1 + if (haystack.length() < needle.length()) { + return -1; + } + // 如果 needle 为空字符串,返回 0 + if (needle.equals("")) { + return 0; } // 如果两者相等,返回 0 if (haystack.equals(needle)) { @@ -3394,72 +3394,6 @@ Output: 0<string - Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析 -/2022/03/07/Leetcode-349-%E4%B8%A4%E4%B8%AA%E6%95%B0%E7%BB%84%E7%9A%84%E4%BA%A4%E9%9B%86-Intersection-of-Two-Arrays-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/ -题目介绍 -给定两个数组
-nums1和nums2,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
-示例
-
-示例 1:
-输入:nums1 = [1,2,2,1], nums2 = [2,2]
-
输出:[2]-
-示例 2:
-输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
-
输出:[9,4]
解释:[4,9] 也是可通过的 -提示:
-
-
1 <= nums1.length, nums2.length <= 1000
-0 <= nums1[i], nums2[i] <= 1000
-
分析与题解
两个数组的交集,最简单就是两层循环了把两个都存在的找出来,不过还有个要去重的问题,稍微思考下可以使用集合
-set来处理,先把一个数组全丢进去,再对比另外一个,如果出现在第一个集合里就丢进一个新的集合,最后转换成数组,这次我稍微取了个巧,因为看到了提示里的条件,两个数组中的元素都是不大于 1000 的,所以就搞了个 1000 长度的数组,如果在第一个数组出现,就在对应的下标设置成 1,如果在第二个数组也出现了就加 1,code
]]>public int[] intersection(int[] nums1, int[] nums2) { - // 大小是 1000 的数组,如果没有提示的条件就没法这么做 - // define a array which size is 1000, and can not be done like this without the condition in notice - int[] inter = new int[1000]; - int[] outer; - int m = 0; - for (int j : nums1) { - // 这里得是设置成 1,因为有可能 nums1 就出现了重复元素,如果直接++会造成结果重复 - // need to be set 1, cause element in nums1 can be duplicated - inter[j] = 1; - } - for (int j : nums2) { - if (inter[j] > 0) { - // 这里可以直接+1,因为后面判断只需要判断大于 1 - // just plus 1, cause we can judge with condition that larger than 1 - inter[j] += 1; - } - } - for (int i = 0; i < inter.length; i++) { - // 统计下元素数量 - // count distinct elements - if (inter[i] > 1) { - m++; - } - } - // initial a array of size m - outer = new int[m]; - m = 0; - for (int i = 0; i < inter.length; i++) { - if (inter[i] > 1) { - // add to outer - outer[m++] = i; - } - } - return outer; - }- -Java -leetcode -- -leetcode -java -题解 -Intersection of Two Arrays - +Leetcode 4 寻找两个正序数组的中位数 ( Median of Two Sorted Arrays *Hard* ) 题解分析 /2022/03/27/Leetcode-4-%E5%AF%BB%E6%89%BE%E4%B8%A4%E4%B8%AA%E6%AD%A3%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0-Median-of-Two-Sorted-Arrays-Hard-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/ @@ -3663,6 +3597,72 @@ maxR[n -矩阵+ Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析 +/2022/03/07/Leetcode-349-%E4%B8%A4%E4%B8%AA%E6%95%B0%E7%BB%84%E7%9A%84%E4%BA%A4%E9%9B%86-Intersection-of-Two-Arrays-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/ +题目介绍 +给定两个数组
+nums1和nums2,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
+示例
+
+示例 1:
+输入:nums1 = [1,2,2,1], nums2 = [2,2]
+
输出:[2]+
+示例 2:
+输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
+
输出:[9,4]
解释:[4,9] 也是可通过的 +提示:
-
+
1 <= nums1.length, nums2.length <= 1000
+0 <= nums1[i], nums2[i] <= 1000
+
分析与题解
两个数组的交集,最简单就是两层循环了把两个都存在的找出来,不过还有个要去重的问题,稍微思考下可以使用集合
+set来处理,先把一个数组全丢进去,再对比另外一个,如果出现在第一个集合里就丢进一个新的集合,最后转换成数组,这次我稍微取了个巧,因为看到了提示里的条件,两个数组中的元素都是不大于 1000 的,所以就搞了个 1000 长度的数组,如果在第一个数组出现,就在对应的下标设置成 1,如果在第二个数组也出现了就加 1,code
]]>public int[] intersection(int[] nums1, int[] nums2) { + // 大小是 1000 的数组,如果没有提示的条件就没法这么做 + // define a array which size is 1000, and can not be done like this without the condition in notice + int[] inter = new int[1000]; + int[] outer; + int m = 0; + for (int j : nums1) { + // 这里得是设置成 1,因为有可能 nums1 就出现了重复元素,如果直接++会造成结果重复 + // need to be set 1, cause element in nums1 can be duplicated + inter[j] = 1; + } + for (int j : nums2) { + if (inter[j] > 0) { + // 这里可以直接+1,因为后面判断只需要判断大于 1 + // just plus 1, cause we can judge with condition that larger than 1 + inter[j] += 1; + } + } + for (int i = 0; i < inter.length; i++) { + // 统计下元素数量 + // count distinct elements + if (inter[i] > 1) { + m++; + } + } + // initial a array of size m + outer = new int[m]; + m = 0; + for (int i = 0; i < inter.length; i++) { + if (inter[i] > 1) { + // add to outer + outer[m++] = i; + } + } + return outer; + }+ +Java +leetcode ++ +leetcode +java +题解 +Intersection of Two Arrays + -Leetcode 83 删除排序链表中的重复元素 ( Remove Duplicates from Sorted List *Easy* ) 题解分析 /2022/03/13/Leetcode-83-%E5%88%A0%E9%99%A4%E6%8E%92%E5%BA%8F%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E9%87%8D%E5%A4%8D%E5%85%83%E7%B4%A0-Remove-Duplicates-from-Sorted-List-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/ @@ -4559,19 +4559,6 @@ public:c++ - C++ 指针使用中的一个小问题 -/2014/12/23/my-new-post/ -在工作中碰到的一点C++指针上的一点小问题 -
-在C++中,应该是从C语言就开始了,除了void型指针之外都是需要有分配对应的内存才可以使用,同时malloc与free成对使用,new与delete成对使用,否则造成内存泄漏。
-]]>- -C++ -- -博客,文章 - +mybatis 的 $ 和 # 是有啥区别 /2020/09/06/mybatis-%E7%9A%84-%E5%92%8C-%E6%98%AF%E6%9C%89%E5%95%A5%E5%8C%BA%E5%88%AB/ @@ -4667,6 +4654,19 @@ public class DynamicSqlSource implements SqlSource {Sql注入 + C++ 指针使用中的一个小问题 +/2014/12/23/my-new-post/ +在工作中碰到的一点C++指针上的一点小问题 +
+在C++中,应该是从C语言就开始了,除了void型指针之外都是需要有分配对应的内存才可以使用,同时malloc与free成对使用,new与delete成对使用,否则造成内存泄漏。
+]]>+ +C++ ++ +博客,文章 + -mybatis 的缓存是怎么回事 /2020/10/03/mybatis-%E7%9A%84%E7%BC%93%E5%AD%98%E6%98%AF%E6%80%8E%E4%B9%88%E5%9B%9E%E4%BA%8B/ @@ -6831,43 +6831,6 @@ hz 10不可变引用- rust学习笔记-所有权一 -/2021/04/18/rust%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/ -最近在看 《rust 权威指南》,还是难度比较大的,它里面的一些概念跟之前的用过的都有比较大的差别 -
比起有 gc 的虚拟机语言,跟像 C 和 C++这种主动释放内存的,rust 有他的独特点,主要是有三条 --
-
- Rust中的每一个值都有一个对应的变量作为它的所有者。 -
- 在同一时间内,值有且只有一个所有者。 -
- 当所有者离开自己的作用域时,它持有的值就会被释放掉。
![]()
这里有两个重点:
- - s 在进入作用域后才变得有效 -
- 它会保持自己的有效性直到自己离开作用域为止 -
然后看个案例
-
-let x = 5; -let y = x;这个其实有两种,一般可以认为比较多实现的会使用 copy on write 之类的,先让两个都指向同一个快 5 的存储,在发生变更后开始正式拷贝,但是涉及到内存处理的便利性,对于这类简单类型,可以直接拷贝
-
但是对于非基础类型
-let s1 = String::from("hello"); -let s2 = s1; - -println!("{}, world!", s1);有可能认为有两种内存分布可能
-
先看下 string 的内存结构![]()
第一种可能是![]()
第二种是![]()
我们来尝试编译下![]()
发现有这个错误,其实在 rust 中let y = x这个行为的实质是移动,在赋值给 y 之后 x 就无效了![]()
这样子就不会造成脱离作用域时,对同一块内存区域的二次释放,如果需要复制,可以使用 clone 方法
-let s1 = String::from("hello"); -let s2 = s1.clone(); - -println!("s1 = {}, s2 = {}", s1, s2);这里其实会有点疑惑,为什么前面的
-]]>x, y的行为跟s1, s2的不一样,其实主要是基本类型和 string 这类的不定大小的类型的内存分配方式不同,x, y这类整型可以直接确定大小,可以直接在栈上分配,而像 string 和其他的变体结构体,其大小都是不能在编译时确定,所以需要在堆上进行分配- -语言 -Rust -- -Rust -所有权 -内存分布 -新语言 - +spark-little-tips /2017/03/28/spark-little-tips/ @@ -6892,6 +6855,43 @@ for _ in data:python + rust学习笔记-所有权一 +/2021/04/18/rust%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/ +最近在看 《rust 权威指南》,还是难度比较大的,它里面的一些概念跟之前的用过的都有比较大的差别 +
比起有 gc 的虚拟机语言,跟像 C 和 C++这种主动释放内存的,rust 有他的独特点,主要是有三条 +-
+
- Rust中的每一个值都有一个对应的变量作为它的所有者。 +
- 在同一时间内,值有且只有一个所有者。 +
- 当所有者离开自己的作用域时,它持有的值就会被释放掉。
![]()
这里有两个重点:
+ - s 在进入作用域后才变得有效 +
- 它会保持自己的有效性直到自己离开作用域为止 +
然后看个案例
+
+let x = 5; +let y = x;这个其实有两种,一般可以认为比较多实现的会使用 copy on write 之类的,先让两个都指向同一个快 5 的存储,在发生变更后开始正式拷贝,但是涉及到内存处理的便利性,对于这类简单类型,可以直接拷贝
+
但是对于非基础类型
+let s1 = String::from("hello"); +let s2 = s1; + +println!("{}, world!", s1);有可能认为有两种内存分布可能
+
先看下 string 的内存结构![]()
第一种可能是![]()
第二种是![]()
我们来尝试编译下![]()
发现有这个错误,其实在 rust 中let y = x这个行为的实质是移动,在赋值给 y 之后 x 就无效了![]()
这样子就不会造成脱离作用域时,对同一块内存区域的二次释放,如果需要复制,可以使用 clone 方法
+let s1 = String::from("hello"); +let s2 = s1.clone(); + +println!("s1 = {}, s2 = {}", s1, s2);这里其实会有点疑惑,为什么前面的
+]]>x, y的行为跟s1, s2的不一样,其实主要是基本类型和 string 这类的不定大小的类型的内存分配方式不同,x, y这类整型可以直接确定大小,可以直接在栈上分配,而像 string 和其他的变体结构体,其大小都是不能在编译时确定,所以需要在堆上进行分配+ +语言 +Rust ++ +Rust +所有权 +内存分布 +新语言 + +spring event 介绍 /2022/01/30/spring-event-%E4%BB%8B%E7%BB%8D/ @@ -7021,6 +7021,22 @@ public:c++ + wordpress 忘记密码的一种解决方法 +/2021/12/05/wordpress-%E5%BF%98%E8%AE%B0%E5%AF%86%E7%A0%81%E7%9A%84%E4%B8%80%E7%A7%8D%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/ +前阵子搭了个 WordPress,但是没怎么用,前两天发现忘了登录密码了,最近不知道是什么情况,chrome 的记住密码跟历史记录感觉有点问题,历史记录丢了不少东西,可能是时间太久了,但是理论上应该有 LRU 这种策略的,有些还比较常用,还有记住密码,因为个人域名都是用子域名分配给各个服务,有些记住了,有些又没记住密码,略蛋疼,所以就找了下这个方案。 +
当然这个方案不是最优的,有很多限制,首先就是要能够登陆 WordPress 的数据库,不然这个方法是没用的。
首先不管用什么方式(别违法)先登陆数据库,选择 WordPress 的数据库,可以看到里面有几个表,我们的目标就是wp_users表,用select查询看下可以看到有用户的数据,如果是像我这样搭着玩的没有创建其他用户的话应该就只有一个用户,那我们的表里的用户数据就只会有一条,当然多条的话可以通过用户名来找![]()
然后可能我这个版本是这样,没有装额外的插件,密码只是经过了 MD5 的单向哈希,所以我们可以设定一个新密码,然后用 MD5 编码后直接更新进去 +
+ +UPDATE wp_users SET user_pass = MD5('123456') WHERE ID = 1;然后就能用自己的账户跟刚才更新的密码登录了。
+]]>+ +小技巧 ++ +WordPress +小技巧 + -swoole-websocket-test /2016/07/13/swoole-websocket-test/ @@ -7146,22 +7162,6 @@ user3:swoole - wordpress 忘记密码的一种解决方法 -/2021/12/05/wordpress-%E5%BF%98%E8%AE%B0%E5%AF%86%E7%A0%81%E7%9A%84%E4%B8%80%E7%A7%8D%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/ -前阵子搭了个 WordPress,但是没怎么用,前两天发现忘了登录密码了,最近不知道是什么情况,chrome 的记住密码跟历史记录感觉有点问题,历史记录丢了不少东西,可能是时间太久了,但是理论上应该有 LRU 这种策略的,有些还比较常用,还有记住密码,因为个人域名都是用子域名分配给各个服务,有些记住了,有些又没记住密码,略蛋疼,所以就找了下这个方案。 -
当然这个方案不是最优的,有很多限制,首先就是要能够登陆 WordPress 的数据库,不然这个方法是没用的。
首先不管用什么方式(别违法)先登陆数据库,选择 WordPress 的数据库,可以看到里面有几个表,我们的目标就是wp_users表,用select查询看下可以看到有用户的数据,如果是像我这样搭着玩的没有创建其他用户的话应该就只有一个用户,那我们的表里的用户数据就只会有一条,当然多条的话可以通过用户名来找![]()
然后可能我这个版本是这样,没有装额外的插件,密码只是经过了 MD5 的单向哈希,所以我们可以设定一个新密码,然后用 MD5 编码后直接更新进去 -
- -UPDATE wp_users SET user_pass = MD5('123456') WHERE ID = 1;然后就能用自己的账户跟刚才更新的密码登录了。
-]]>- -小技巧 -- -WordPress -小技巧 - +《垃圾回收算法手册读书》笔记之整理算法 /2021/03/07/%E3%80%8A%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E6%89%8B%E5%86%8C%E8%AF%BB%E4%B9%A6%E3%80%8B%E7%AC%94%E8%AE%B0%E4%B9%8B%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95/ @@ -7184,6 +7184,19 @@ user3:jvm + 上次的其他 外行聊国足 +/2022/03/06/%E4%B8%8A%E6%AC%A1%E7%9A%84%E5%85%B6%E4%BB%96-%E5%A4%96%E8%A1%8C%E8%81%8A%E5%9B%BD%E8%B6%B3/ +上次本来想在换车牌后面聊下这个话题,为啥要聊这个话题呢,也很简单,在地铁上看到一对猜测是情侣或者比较关系好的男女同学在聊,因为是这位男同学是大学学的工科,然后自己爱好设计绘画相关的,可能还以此赚了点钱,在地铁上讨论男的要不要好好努力把大学课程完成好,大致的观点是没必要,本来就不适合,这一段我就不说了,恋爱人的嘴,信你个鬼。 +
后面男的说在家里又跟他爹吵了关于男足的,估计是那次输了越南,实话说我不是个足球迷,对各方面技术相关也不熟,只是对包括这个人的解释和网上一些观点的看法,纯主观,这次地铁上这位说的大概意思是足球这个训练什么的很难的,要想赢越南也很难的,不是我们能嘴炮的;在网上看到一个赞同数很多的一个回答,说什么中国是个体育弱国,但是由于有一些乒乓球,跳水等小众项目比较厉害,让民众给误解了,首先我先来反驳下这个偷换概念的观点,第一所谓的体育弱国,跟我们觉得足球不应该这么差没半毛钱关系,因为体育弱国,我们的足球本来就不是顶尖的,也并不是去跟顶尖的球队去争,以足球为例,跟巴西,阿根廷,英国,德国,西班牙,意大利,法国这些足球强国,去比较,我相信没有一个足球迷会这么去做对比,因为我们足球历史最高排名是 1998 年的 37 名,最差是 100 名,把能数出来的强队都数完,估计都还不会到 37,所以根本没有跟强队去做对比,第二体育弱国,我们的体育投入是在逐年降低吗,我们是因战乱没法好好训练踢球?还是这帮傻逼就不争气,前面也说了我们足球世界排名最高 37,最低 100,那么前阵子我们输的越南是第几,目前我们的排名 77 名,越南 92 名,看明白了么,轮排名我们都不至于输越南,然后就是这个排名,这也是我想回应那位地铁上的兄弟,我觉得除了造核弹这种高精尖技术,绝大部分包含足球这类运动,遵循类二八原则,比如满分是 100 分,从 80 提到 90 分或者 90 分提到 100 分非常难,30 分提到 40 分,50 分提到 60 分我觉得都是可以凭后天努力达成的,基本不受天赋限制,这里可以以篮球来类比下,相对足球的确篮球没有那么火,或者行业市值没法比,但是也算是相对大众了,中国在篮球方面相对比较好一点,在 08 年奥运会冲进过八强,那也不是唯一的巅峰,但是我说这个其实是想说明两方面的事情,第一,像篮球一样,状态是有起起伏伏,排名也会变动,但是我觉得至少能维持一个相对稳定的总体排名和持平或者上升的趋势,这恰恰是我们这种所谓的“体育弱国”应该走的路线,第二就是去支持我的类二八原则的,可以看到我们的篮球这两年也很垃圾,排名跌到 29 了,那问题我觉得跟足球是一样的,就是不能脚踏实地,如斯科拉说的,中国篮球太缺少竞争,打得好不好都是这些人打,打输了还是照样拿钱,相对足球,篮球的技术我还是懂一些的,对比 08 年的中国男篮,的确像姚明跟王治郅这样的天赋型+努力型球员少了以后竞争力下降在所难免,但是去对比下基本功,传球,投篮,罚球稳定性,也完全不是一个水平的,这些就是我说的,可以通过努力训练拿 80 分的,只要拿到 80 分,甚至只要拿到 60 分,我觉得应该就还算对得起球迷了,就像 NBA 里球队也会有核心球员的更替,战绩起起伏伏,但是基本功这东西,防守积极性,我觉得不随核心球员的变化而变化,就像姚明这样的天赋,其实他应该还有一些先天缺陷,大脚趾较长等,但是他从 CBA 到 NBA,在 NBA 适应并且打成顶尖中锋,离不开刻苦训练,任何的成功都不是纯天赋的,必须要付出足够的努力。
说回足球,如果像前面那么洗地(体育弱国),那能给我维持住一个稳定的排名我也能接受,问题是我们的经济物质资源比 2000 年前应该有了质的变化,身体素质也越来越好,即使是体育弱国,这么继续走下坡路,半死不活的,不觉得是打了自己的脸么。足球也需要基本功,基本的体能,力量这些,看看现在这些国足运动员的体型,对比下女足,说实话,如果男足这些运动员都练得不错的体脂率,耐力等,成绩即使不好,也不会比现在更差。
纯主观吐槽,勿喷。 +]]>+ +生活 +运动 ++ +生活 + +介绍一下 RocketMQ /2020/06/21/%E4%BB%8B%E7%BB%8D%E4%B8%80%E4%B8%8B-RocketMQ/ @@ -7318,6 +7331,25 @@ user3:杀人诛心 + 关于读书打卡与分享 +/2021/02/07/%E5%85%B3%E4%BA%8E%E8%AF%BB%E4%B9%A6%E6%89%93%E5%8D%A1%E4%B8%8E%E5%88%86%E4%BA%AB/ +最近群里大佬发起了一个读书打卡活动,需要每天读一会书,在群里打卡分享感悟,争取一个月能读完一本书,说实话一天十分钟的读书时间倒是问题不大,不过每天都要打卡,而且一个月要读完一本书,其实难度还是有点大的,不过也想试试看。 +
之前某某老大给自己立了个 flag,说要读一百本书,这对我来说挺难实现的,一则我也不喜欢书只读一小半,二则感觉对于喜欢看的内容范围还是比较有限制,可能也算是比较矫情,不爱追热门的各类东西,因为往往会有一些跟大众不一致的观点看法,显得格格不入。所以还是这个打卡活动可能会比较适合我,书是人类进步的阶梯。
到现在是打卡了三天了,读的主要是白岩松的《幸福了吗》,对于白岩松,我们这一代人是比较熟悉,并且整体印象比较不错的一个央视主持人,从《焦点访谈》开始,到疫情期间的各类一线节目,可能对我来说是个三观比较正,敢于说一些真话的主持人,这中间其实是有个空档期,没怎么看电视,也不太关注了,只是在疫情期间的节目,还是一如既往地给人一种可靠的感觉,正好有一次偶然微信读书推荐了白岩松的这本书,就看了一部分,正好这次继续往下看,因为相对来讲不会很晦涩,并且从这位知名央视主持人的角度分享他的过往和想法看法,还是比较有意思的。
从对汶川地震,08 年奥运等往事的回忆和一些细节的呈现,也让我了解比较多当时所不知道的,特别是汶川地震,那时的我还在读高中,真的是看着电视,作为“猛男”都忍不住泪目了,共和国之殇,多难兴邦,但是这对于当事人来说,都是一场醒不过来的噩梦。
然后是对于足球的热爱,其实光这个就能掰扯很多,因为我不爱足球,只爱篮球,其中原因有的没的也挺多可以说的,但是看了他的书,才能比较深入的了解一个足球迷,对足球,对中国足球,对世界杯,对阿根廷的感情。
接下去还是想能继续坚持下去,加油! +]]>+ +生活 +读后感 +白岩松 +幸福了吗 ++ +读后感 +读书 +打卡 +幸福了吗 +足球 + -关于公共交通再吐个槽 /2021/03/21/%E5%85%B3%E4%BA%8E%E5%85%AC%E5%85%B1%E4%BA%A4%E9%80%9A%E5%86%8D%E5%90%90%E4%B8%AA%E6%A7%BD/ @@ -7340,25 +7372,6 @@ user3:健康码 - 关于读书打卡与分享 -/2021/02/07/%E5%85%B3%E4%BA%8E%E8%AF%BB%E4%B9%A6%E6%89%93%E5%8D%A1%E4%B8%8E%E5%88%86%E4%BA%AB/ -最近群里大佬发起了一个读书打卡活动,需要每天读一会书,在群里打卡分享感悟,争取一个月能读完一本书,说实话一天十分钟的读书时间倒是问题不大,不过每天都要打卡,而且一个月要读完一本书,其实难度还是有点大的,不过也想试试看。 -
之前某某老大给自己立了个 flag,说要读一百本书,这对我来说挺难实现的,一则我也不喜欢书只读一小半,二则感觉对于喜欢看的内容范围还是比较有限制,可能也算是比较矫情,不爱追热门的各类东西,因为往往会有一些跟大众不一致的观点看法,显得格格不入。所以还是这个打卡活动可能会比较适合我,书是人类进步的阶梯。
到现在是打卡了三天了,读的主要是白岩松的《幸福了吗》,对于白岩松,我们这一代人是比较熟悉,并且整体印象比较不错的一个央视主持人,从《焦点访谈》开始,到疫情期间的各类一线节目,可能对我来说是个三观比较正,敢于说一些真话的主持人,这中间其实是有个空档期,没怎么看电视,也不太关注了,只是在疫情期间的节目,还是一如既往地给人一种可靠的感觉,正好有一次偶然微信读书推荐了白岩松的这本书,就看了一部分,正好这次继续往下看,因为相对来讲不会很晦涩,并且从这位知名央视主持人的角度分享他的过往和想法看法,还是比较有意思的。
从对汶川地震,08 年奥运等往事的回忆和一些细节的呈现,也让我了解比较多当时所不知道的,特别是汶川地震,那时的我还在读高中,真的是看着电视,作为“猛男”都忍不住泪目了,共和国之殇,多难兴邦,但是这对于当事人来说,都是一场醒不过来的噩梦。
然后是对于足球的热爱,其实光这个就能掰扯很多,因为我不爱足球,只爱篮球,其中原因有的没的也挺多可以说的,但是看了他的书,才能比较深入的了解一个足球迷,对足球,对中国足球,对世界杯,对阿根廷的感情。
接下去还是想能继续坚持下去,加油! -]]>- -生活 -读后感 -白岩松 -幸福了吗 -- -读后感 -读书 -打卡 -幸福了吗 -足球 - +周末我在老丈人家打了天小工 /2020/08/16/%E5%91%A8%E6%9C%AB%E6%88%91%E5%9C%A8%E8%80%81%E4%B8%88%E4%BA%BA%E5%AE%B6%E6%89%93%E4%BA%86%E5%A4%A9%E5%B0%8F%E5%B7%A5/ @@ -7378,6 +7391,44 @@ user3:干活 + +分享记录一下一个 scp 操作方法 +/2022/02/06/%E5%88%86%E4%BA%AB%E8%AE%B0%E5%BD%95%E4%B8%80%E4%B8%8B%E4%B8%80%E4%B8%AA-scp-%E6%93%8D%E4%BD%9C%E6%96%B9%E6%B3%95/ +scp 是个在服务器之间拷贝文件的一个常用命令,有时候有个场景是比如我们需要拷贝一些带有共同前缀的文件,但是有一个问题是比如我们有使用 zsh 的话,会出现一个报错, + +
+zsh: no matches found: root@100.100.100.100://root/prefix*这里就比较奇怪了,这个前缀的文件肯定是有的,这里其实是由于 zsh 会对
+*进行展开,这个可以在例如ls命令在使用中就可以发现zsh有这个特性
需要使用双引号或单引号将路径包起来或者在*之前加反斜杠\来阻止对*展开和转义
+scp root@100.100.100.100://root/prefix* .通过使用双引号
+"进行转义
+scp root@100.100.100.100:"//root/prefix*" .或者可以将 shell 从 zsh 切换成 bash
+]]>+ +shell +小技巧 ++ +scp ++ 分享记录一下一个 git 操作方法 +/2022/02/06/%E5%88%86%E4%BA%AB%E8%AE%B0%E5%BD%95%E4%B8%80%E4%B8%8B%E4%B8%80%E4%B8%AA-git-%E6%93%8D%E4%BD%9C%E6%96%B9%E6%B3%95/ +前阵子一个同事因为发现某个分支上的代码好像有缺失导致无法正常运行,然后就对比了下把缺失的代码从另一个分支上拷了过来,可能有所欠考虑,不过主要是说下操作过程和最后的处理方法,这位同学的操作是改一些代码commit 一下,这样的 commit 了大概五六次,并且已经 push 到了远端,然后就在想要怎么去处理,在本地可以 reset,已经到远端了,一个很不优雅的操作就是本地 reset 了用 force push,这个当然是不可取的,然后就是 revert 了,但是又已经 commit 了好几次了,网上看了下,好像处理方法还挺成熟的,git revert 命令本质上就是一个逆向的 git cherry-pick 操作。 它将你提交中的变更的以完全相反的方式的应用到一个新创建的提交中,本质上就是撤销或者倒转。可以理解为就是提交一个反向的操作,这里其实我们可以用 +range revert来进行git revert, 用法就是 +
+git revert OLDER_COMMIT^..NEWER_COMMIT这样就可以解决上面的问题了,但是还有个问题是这样会根据前面的 commit 数量提交对应数量个 revert commit 会显得比较乱,如果要比较干净的 commit 历史,
+
可以看下git revert命令说明![]()
然后就可以用-n参数,表示不自动提交
+ + +]]>git revert -n OLDER_COMMIT^..NEWER_COMMIT +git commit -m "revert OLDER_COMMIT to NEWER_COMMIT"+ +git +小技巧 ++ +git + -在老丈人家的小工记三 /2020/09/13/%E5%9C%A8%E8%80%81%E4%B8%88%E4%BA%BA%E5%AE%B6%E7%9A%84%E5%B0%8F%E5%B7%A5%E8%AE%B0%E4%B8%89/ @@ -7480,38 +7531,6 @@ user3:跑步 - -分享记录一下一个 git 操作方法 -/2022/02/06/%E5%88%86%E4%BA%AB%E8%AE%B0%E5%BD%95%E4%B8%80%E4%B8%8B%E4%B8%80%E4%B8%AA-git-%E6%93%8D%E4%BD%9C%E6%96%B9%E6%B3%95/ -前阵子一个同事因为发现某个分支上的代码好像有缺失导致无法正常运行,然后就对比了下把缺失的代码从另一个分支上拷了过来,可能有所欠考虑,不过主要是说下操作过程和最后的处理方法,这位同学的操作是改一些代码commit 一下,这样的 commit 了大概五六次,并且已经 push 到了远端,然后就在想要怎么去处理,在本地可以 reset,已经到远端了,一个很不优雅的操作就是本地 reset 了用 force push,这个当然是不可取的,然后就是 revert 了,但是又已经 commit 了好几次了,网上看了下,好像处理方法还挺成熟的,git revert 命令本质上就是一个逆向的 git cherry-pick 操作。 它将你提交中的变更的以完全相反的方式的应用到一个新创建的提交中,本质上就是撤销或者倒转。可以理解为就是提交一个反向的操作,这里其实我们可以用 -range revert来进行git revert, 用法就是 -
-git revert OLDER_COMMIT^..NEWER_COMMIT这样就可以解决上面的问题了,但是还有个问题是这样会根据前面的 commit 数量提交对应数量个 revert commit 会显得比较乱,如果要比较干净的 commit 历史,
-
可以看下git revert命令说明![]()
然后就可以用-n参数,表示不自动提交
- - -]]>git revert -n OLDER_COMMIT^..NEWER_COMMIT -git commit -m "revert OLDER_COMMIT to NEWER_COMMIT"- -git -小技巧 -- -git -- 上次的其他 外行聊国足 -/2022/03/06/%E4%B8%8A%E6%AC%A1%E7%9A%84%E5%85%B6%E4%BB%96-%E5%A4%96%E8%A1%8C%E8%81%8A%E5%9B%BD%E8%B6%B3/ -上次本来想在换车牌后面聊下这个话题,为啥要聊这个话题呢,也很简单,在地铁上看到一对猜测是情侣或者比较关系好的男女同学在聊,因为是这位男同学是大学学的工科,然后自己爱好设计绘画相关的,可能还以此赚了点钱,在地铁上讨论男的要不要好好努力把大学课程完成好,大致的观点是没必要,本来就不适合,这一段我就不说了,恋爱人的嘴,信你个鬼。 -
后面男的说在家里又跟他爹吵了关于男足的,估计是那次输了越南,实话说我不是个足球迷,对各方面技术相关也不熟,只是对包括这个人的解释和网上一些观点的看法,纯主观,这次地铁上这位说的大概意思是足球这个训练什么的很难的,要想赢越南也很难的,不是我们能嘴炮的;在网上看到一个赞同数很多的一个回答,说什么中国是个体育弱国,但是由于有一些乒乓球,跳水等小众项目比较厉害,让民众给误解了,首先我先来反驳下这个偷换概念的观点,第一所谓的体育弱国,跟我们觉得足球不应该这么差没半毛钱关系,因为体育弱国,我们的足球本来就不是顶尖的,也并不是去跟顶尖的球队去争,以足球为例,跟巴西,阿根廷,英国,德国,西班牙,意大利,法国这些足球强国,去比较,我相信没有一个足球迷会这么去做对比,因为我们足球历史最高排名是 1998 年的 37 名,最差是 100 名,把能数出来的强队都数完,估计都还不会到 37,所以根本没有跟强队去做对比,第二体育弱国,我们的体育投入是在逐年降低吗,我们是因战乱没法好好训练踢球?还是这帮傻逼就不争气,前面也说了我们足球世界排名最高 37,最低 100,那么前阵子我们输的越南是第几,目前我们的排名 77 名,越南 92 名,看明白了么,轮排名我们都不至于输越南,然后就是这个排名,这也是我想回应那位地铁上的兄弟,我觉得除了造核弹这种高精尖技术,绝大部分包含足球这类运动,遵循类二八原则,比如满分是 100 分,从 80 提到 90 分或者 90 分提到 100 分非常难,30 分提到 40 分,50 分提到 60 分我觉得都是可以凭后天努力达成的,基本不受天赋限制,这里可以以篮球来类比下,相对足球的确篮球没有那么火,或者行业市值没法比,但是也算是相对大众了,中国在篮球方面相对比较好一点,在 08 年奥运会冲进过八强,那也不是唯一的巅峰,但是我说这个其实是想说明两方面的事情,第一,像篮球一样,状态是有起起伏伏,排名也会变动,但是我觉得至少能维持一个相对稳定的总体排名和持平或者上升的趋势,这恰恰是我们这种所谓的“体育弱国”应该走的路线,第二就是去支持我的类二八原则的,可以看到我们的篮球这两年也很垃圾,排名跌到 29 了,那问题我觉得跟足球是一样的,就是不能脚踏实地,如斯科拉说的,中国篮球太缺少竞争,打得好不好都是这些人打,打输了还是照样拿钱,相对足球,篮球的技术我还是懂一些的,对比 08 年的中国男篮,的确像姚明跟王治郅这样的天赋型+努力型球员少了以后竞争力下降在所难免,但是去对比下基本功,传球,投篮,罚球稳定性,也完全不是一个水平的,这些就是我说的,可以通过努力训练拿 80 分的,只要拿到 80 分,甚至只要拿到 60 分,我觉得应该就还算对得起球迷了,就像 NBA 里球队也会有核心球员的更替,战绩起起伏伏,但是基本功这东西,防守积极性,我觉得不随核心球员的变化而变化,就像姚明这样的天赋,其实他应该还有一些先天缺陷,大脚趾较长等,但是他从 CBA 到 NBA,在 NBA 适应并且打成顶尖中锋,离不开刻苦训练,任何的成功都不是纯天赋的,必须要付出足够的努力。
说回足球,如果像前面那么洗地(体育弱国),那能给我维持住一个稳定的排名我也能接受,问题是我们的经济物质资源比 2000 年前应该有了质的变化,身体素质也越来越好,即使是体育弱国,这么继续走下坡路,半死不活的,不觉得是打了自己的脸么。足球也需要基本功,基本的体能,力量这些,看看现在这些国足运动员的体型,对比下女足,说实话,如果男足这些运动员都练得不错的体脂率,耐力等,成绩即使不好,也不会比现在更差。
纯主观吐槽,勿喷。 -]]>- -生活 -运动 -- -生活 - +看完了扫黑风暴,聊聊感想 /2021/10/24/%E7%9C%8B%E5%AE%8C%E4%BA%86%E6%89%AB%E9%BB%91%E9%A3%8E%E6%9A%B4-%E8%81%8A%E8%81%8A%E6%84%9F%E6%83%B3/ @@ -7529,55 +7548,143 @@ git commit -m "revert OLDER_COMMIT to NEWER_COMMIT"public static void main(String[] args) throws InterruptedException, MQClientException { - - /* - * Instantiate with specified consumer group name. - * 首先是new 一个对象出来,然后指定 Consumer 的 Group - * 同一类Consumer的集合,这类Consumer通常消费同一类消息且消费逻辑一致。消费者组使得在消息消费方面,实现负载均衡和容错的目标变得非常容易。要注意的是,消费者组的消费者实例必须订阅完全相同的Topic。RocketMQ 支持两种消息模式:集群消费(Clustering)和广播消费(Broadcasting)。 - */ - DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4"); - - /* - * Specify name server addresses. - * <p/> - * 这里可以通知指定环境变量或者设置对象参数的形式指定名字空间服务的地址 - * - * Alternatively, you may specify name server addresses via exporting environmental variable: NAMESRV_ADDR - * <pre> - * {@code - * consumer.setNamesrvAddr("name-server1-ip:9876;name-server2-ip:9876"); - * } - * </pre> - */ - - /* - * Specify where to start in case the specified consumer group is a brand new one. - * 指定消费起始点 - */ - consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); - - /* - * Subscribe one more more topics to consume. - * 指定订阅的 topic 跟 tag,注意后面的是个表达式,可以以 tag1 || tag2 || tag3 传入 - */ - consumer.subscribe("TopicTest", "*"); - - /* - * Register callback to execute on arrival of messages fetched from brokers. - * 注册具体获得消息后的处理方法 - */ - consumer.registerMessageListener(new MessageListenerConcurrently() { - - @Override - public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, - ConsumeConcurrentlyContext context) { - System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs); - return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; - } +搬运两个 StackOverflow 上的 Mysql 编码相关的问题解答 +/2022/01/16/%E6%90%AC%E8%BF%90%E4%B8%A4%E4%B8%AA-StackOverflow-%E4%B8%8A%E7%9A%84-Mysql-%E7%BC%96%E7%A0%81%E7%9B%B8%E5%85%B3%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94/ +Mysql 字符编码和排序规则 +这个一直是属于一知半解的状态,知道 utf8 跟 utf8mb4 的区别主要是能不能支持 emoji,但是具体后面配置的排序规则是用来干嘛,或者有什么区别,应该使用哪个,所以在 stackoverflow 上找了下,有两个比较不错的解答,就搬过来并且配合机翻做了点修改
+原文
For those people still arriving at this question in 2020 or later, there are newer options that may be better than both of these. For example,
+utf8mb4_0900_ai_ci.All these collations are for the UTF-8 character encoding. The differences are in how text is sorted and compared.
+
+_unicode_ciand_general_ciare two different sets of rules for sorting and comparing text according to the way we expect. Newer versions of MySQL introduce new sets of rules, too, such as_0900_ai_cifor equivalent rules based on Unicode 9.0 - and with no equivalent_general_civariant. People reading this now should probably use one of these newer collations instead of either_unicode_cior_general_ci. The description of those older collations below is provided for interest only.MySQL is currently transitioning away from an older, flawed UTF-8 implementation. For now, you need to use utf8mb4 instead of utf8 for the character encoding part, to ensure you are getting the fixed version. The flawed version remains for backward compatibility, though it is being deprecated.
+Key differences
+
+utf8mb4_unicode_ciis based on the official Unicode rules for universal sorting and comparison, which sorts accurately in a wide range of languages.
+utf8mb4_general_ciis a simplified set of sorting rules which aims to do as well as it can while taking many short-cuts designed to improve speed. It does not follow the Unicode rules and will result in undesirable sorting or comparison in some situations, such as when using particular languages or characters.On modern servers, this performance boost will be all but negligible. It was devised in a time when servers had a tiny fraction of the CPU performance of today’s computers.
+Benefits of
+utf8mb4_unicode_cioverutf8mb4_general_ciutf8mb4_unicode_ci, which uses the Unicode rules for sorting and comparison, employs a fairly complex algorithm for correct sorting in a wide range of languages and when using a wide range of special characters. These rules need to take into account language-specific conventions; not everybody sorts their characters in what we would call ‘alphabetical order’.
+As far as Latin (ie “European”) languages go, there is not much difference between the Unicode sorting and the simplified
+utf8mb4_general_cisorting in MySQL, but there are still a few differences:For examples, the Unicode collation sorts “ß” like “ss”, and “Œ” like “OE” as people using those characters would normally want, whereas
+utf8mb4_general_cisorts them as single characters (presumably like “s” and “e” respectively).Some Unicode characters are defined as ignorable, which means they shouldn’t count toward the sort order and the comparison should move on to the next character instead.
+utf8mb4_unicode_cihandles these properly.In non-latin languages, such as Asian languages or languages with different alphabets, there may be a lot more differences between Unicode sorting and the simplified
+utf8mb4_general_cisorting. The suitability ofutf8mb4_general_ciwill depend heavily on the language used. For some languages, it’ll be quite inadequate.What should you use?
+There is almost certainly no reason to use
+utf8mb4_general_cianymore, as we have left behind the point where CPU speed is low enough that the performance difference would be important. Your database will almost certainly be limited by other bottlenecks than this.In the past, some people recommended to use
+utf8mb4_general_ciexcept when accurate sorting was going to be important enough to justify the performance cost. Today, that performance cost has all but disappeared, and developers are treating internationalization more seriously.There’s an argument to be made that if speed is more important to you than accuracy, you may as well not do any sorting at all. It’s trivial to make an algorithm faster if you do not need it to be accurate. So,
+utf8mb4_general_ciis a compromise that’s probably not needed for speed reasons and probably also not suitable for accuracy reasons.One other thing I’ll add is that even if you know your application only supports the English language, it may still need to deal with people’s names, which can often contain characters used in other languages in which it is just as important to sort correctly. Using the Unicode rules for everything helps add peace of mind that the very smart Unicode people have worked very hard to make sorting work properly.
+What the parts mean
+Firstly, ci is for case-insensitive sorting and comparison. This means it’s suitable for textual data, and case is not important. The other types of collation are cs (case-sensitive) for textual data where case is important, and bin, for where the encoding needs to match, bit for bit, which is suitable for fields which are really encoded binary data (including, for example, Base64). Case-sensitive sorting leads to some weird results and case-sensitive comparison can result in duplicate values differing only in letter case, so case-sensitive collations are falling out of favor for textual data - if case is significant to you, then otherwise ignorable punctuation and so on is probably also significant, and a binary collation might be more appropriate.
+Next, unicode or general refers to the specific sorting and comparison rules - in particular, the way text is normalized or compared. There are many different sets of rules for the utf8mb4 character encoding, with unicode and general being two that attempt to work well in all possible languages rather than one specific one. The differences between these two sets of rules are the subject of this answer. Note that unicode uses rules from Unicode 4.0. Recent versions of MySQL add the rulesets unicode_520 using rules from Unicode 5.2, and 0900 (dropping the “unicode_” part) using rules from Unicode 9.0.
+And lastly, utf8mb4 is of course the character encoding used internally. In this answer I’m talking only about Unicode based encodings.
+翻译
对于那些在 2020 年或之后仍会遇到这个问题的人,有可能比这两个更好的新选项。例如,
+utf8mb4_0900_ai_ci。所有这些排序规则都用于 UTF-8 字符编码。不同之处在于文本的排序和比较方式。
+
+_unicode_ci和_general_ci是两组不同的规则,用于按照我们期望的方式对文本进行排序和比较。较新版本的 MySQL 也引入了新的规则集,例如_0900_ai_ci用于基于 Unicode 9.0 的等效规则 - 并且没有等效的_general_ci变体。现在阅读本文的人可能应该使用这些较新的排序规则之一,而不是_unicode_ci或_general_ci。下面对那些较旧的排序规则的描述仅供参考。MySQL 目前正在从旧的、有缺陷的 UTF-8 实现过渡。现在,您需要使用
+utf8mb4而不是utf8作为字符编码部分,以确保您获得的是固定版本。有缺陷的版本仍然是为了向后兼容,尽管它已被弃用。主要区别
+
+utf8mb4_unicode_ci基于官方 Unicode 规则进行通用排序和比较,可在多种语言中准确排序。
+utf8mb4_general_ci是一组简化的排序规则,旨在尽其所能,同时采用许多旨在提高速度的捷径。它不遵循 Unicode 规则,并且在某些情况下会导致不希望的排序或比较,例如在使用特定语言或字符时。在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器的 CPU 性能只有当今计算机的一小部分时设计的。
+
+utf8mb4_unicode_ci相对于utf8mb4_general_ci的优势
+utf8mb4_unicode_ci使用 Unicode 规则进行排序和比较,采用相当复杂的算法在多种语言中以及在使用多种特殊字符时进行正确排序。这些规则需要考虑特定语言的约定;不是每个人都按照我们所说的“字母顺序”对他们的字符进行排序。就拉丁语(即“欧洲”)语言而言,Unicode 排序和 MySQL 中简化的
+utf8mb4_general_ci排序没有太大区别,但仍有一些区别:例如,Unicode 排序规则将“ß”排序为“ss”,将“Œ”排序为“OE”,因为使用这些字符的人通常需要这些字符,而
+utf8mb4_general_ci将它们排序为单个字符(大概分别像“s”和“e” )。一些 Unicode 字符被定义为可忽略,这意味着它们不应该计入排序顺序,并且比较应该转到下一个字符。
+utf8mb4_unicode_ci正确处理这些。在非拉丁语言中,例如亚洲语言或具有不同字母的语言,Unicode 排序和简化的
+utf8mb4_general_ci排序之间可能存在更多差异。utf8mb4_general_ci的适用性在很大程度上取决于所使用的语言。对于某些语言,这将是非常不充分的。你应该用什么?
+几乎可以肯定没有理由再使用
+utf8mb4_general_ci,因为我们已经将 CPU 速度低到会严重影响性能表现的时代远抛在脑后了。您的数据库几乎肯定会受到除此之外的其他瓶颈的限制。过去,有些人建议使用
+utf8mb4_general_ci,除非准确排序足够重要以证明性能成本是合理的。如今,这种性能成本几乎消失了,开发人员正在更加认真地对待国际化。有一个论点是,如果速度对您来说比准确性更重要,那么您可能根本不进行任何排序。如果您不需要准确的算法,那么使算法更快是微不足道的。因此,
+utf8mb4_general_ci是一种折衷方案,出于速度原因可能不需要,也可能出于准确性原因也不适合。我要补充的另一件事是,即使您知道您的应用程序仅支持英语,它可能仍需要处理人名,这些人名通常包含其他语言中使用的字符,在这些语言中正确排序同样重要.对所有事情都使用 Unicode 规则有助于让您更加安心,因为非常聪明的 Unicode 人员已经非常努力地工作以使排序正常工作。
+其余各个部分是什么意思
+首先,
+ci用于不区分大小写的排序和比较。这意味着它适用于文本数据,大小写并不重要。其他类型的排序规则是cs(区分大小写),用于区分大小写的文本数据,以及bin,用于编码需要匹配的地方,逐位匹配,适用于真正编码二进制数据的字段(包括,用于例如,Base64)。区分大小写的排序会导致一些奇怪的结果,区分大小写的比较可能会导致重复值仅在字母大小写上有所不同,因此区分大小写的排序规则对文本数据不受欢迎 - 如果大小写对您很重要,那么标点符号就可以忽略等等可能也很重要,二进制排序规则可能更合适。接下来,unicode 或general 指的是具体的排序和比较规则——特别是文本被规范化或比较的方式。 utf8mb4 字符编码有许多不同的规则集,其中 unicode 和 general 是两种,它们试图在所有可能的语言中都很好地工作,而不是在一种特定的语言中。这两组规则之间的差异是此答案的主题。请注意,unicode 使用 Unicode 4.0 中的规则。 MySQL 的最新版本使用 Unicode 5.2 的规则添加规则集 unicode_520,使用 Unicode 9.0 的规则添加 0900(删除“unicode_”部分)。
+最后,utf8mb4 当然是内部使用的字符编码。在这个答案中,我只谈论基于 Unicode 的编码。
+utf8 和 utf8mb4 编码有什么区别
原文
UTF-8is a variable-length encoding. In the case of UTF-8, this means that storing one code point requires one to four bytes. However, MySQL’s encoding called “utf8” (alias of “utf8mb3”) only stores a maximum of three bytes per code point.
+So the character set “utf8”/“utf8mb3” cannot store all Unicode code points: it only supports the range 0x000 to 0xFFFF, which is called the “Basic Multilingual Plane“. See also Comparison of Unicode encodings.
+This is what (a previous version of the same page at)the MySQL documentationhas to say about it:
++
+The character set named utf8[/utf8mb3] uses a maximum of three bytes per character and contains only BMP characters. As of MySQL 5.5.3, the utf8mb4 character set uses a maximum of four bytes per character supports supplemental characters:
+-
+
- For a BMP character, utf8[/utf8mb3] and utf8mb4 have identical storage characteristics: same code values, same encoding, same length. +
- For a supplementary character, utf8[/utf8mb3] cannot store the character at all, while utf8mb4 requires four bytes to store it. Since utf8[/utf8mb3] cannot store the character at all, you do not have any supplementary characters in utf8[/utf8mb3] columns and you need not worry about converting characters or losing data when upgrading utf8[/utf8mb3] data from older versions of MySQL. +
So if you want your column to support storing characters lying outside the BMP (and you usually want to), such as emoji, use “utf8mb4”. See also What are the most common non-BMP Unicode characters in actual use?.
+译文
UTF-8 是一种可变长度编码。对于 UTF-8,这意味着存储一个代码点需要一到四个字节。但是,MySQL 的编码称为“utf8”(“utf8mb3”的别名)每个代码点最多只能存储三个字节。
+所以字符集“utf8”/“utf8mb3”不能存储所有的Unicode码位:它只支持0x000到0xFFFF的范围,被称为“基本多语言平面”。另请参阅 Unicode 编码比较。
+这就是(同一页面的先前版本)MySQL 文档 不得不说的:
++
+名为 utf8[/utf8mb3] 的字符集每个字符最多使用三个字节,并且仅包含 BMP 字符。从 MySQL 5.5.3 开始,utf8mb4 字符集每个字符最多使用四个字节,支持补充字符:
+-
+
- 对于 BMP 字符,utf8[/utf8mb3] 和 utf8mb4 具有相同的存储特性:相同的代码值、相同的编码、相同的长度。 +
- 对于补充字符,utf8[/utf8mb3] 根本无法存储该字符,而 utf8mb4 需要四个字节来存储它。由于 utf8[/utf8mb3] 根本无法存储字符,因此您在 utf8[/utf8mb3] 列中没有任何补充字符,您不必担心从旧版本升级 utf8[/utf8mb3] 数据时转换字符或丢失数据mysql。 +
因此,如果您希望您的列支持存储位于 BMP 之外的字符(并且您通常希望这样做),例如 emoji,请使用“utf8mb4”。另请参阅
+ +]]>+ +Mysql ++ +mysql +字符集 +编码 +utf8 +utf8mb4 +utf8mb4_0900_ai_ci +utf8mb4_unicode_ci +utf8mb4_general_ci ++ -聊一下 RocketMQ 的 DefaultMQPushConsumer 源码 +/2020/06/26/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84-Consumer/ +首先看下官方的小 demo + public static void main(String[] args) throws InterruptedException, MQClientException { + + /* + * Instantiate with specified consumer group name. + * 首先是new 一个对象出来,然后指定 Consumer 的 Group + * 同一类Consumer的集合,这类Consumer通常消费同一类消息且消费逻辑一致。消费者组使得在消息消费方面,实现负载均衡和容错的目标变得非常容易。要注意的是,消费者组的消费者实例必须订阅完全相同的Topic。RocketMQ 支持两种消息模式:集群消费(Clustering)和广播消费(Broadcasting)。 + */ + DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4"); + + /* + * Specify name server addresses. + * <p/> + * 这里可以通知指定环境变量或者设置对象参数的形式指定名字空间服务的地址 + * + * Alternatively, you may specify name server addresses via exporting environmental variable: NAMESRV_ADDR + * <pre> + * {@code + * consumer.setNamesrvAddr("name-server1-ip:9876;name-server2-ip:9876"); + * } + * </pre> + */ + + /* + * Specify where to start in case the specified consumer group is a brand new one. + * 指定消费起始点 + */ + consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); + + /* + * Subscribe one more more topics to consume. + * 指定订阅的 topic 跟 tag,注意后面的是个表达式,可以以 tag1 || tag2 || tag3 传入 + */ + consumer.subscribe("TopicTest", "*"); + + /* + * Register callback to execute on arrival of messages fetched from brokers. + * 注册具体获得消息后的处理方法 + */ + consumer.registerMessageListener(new MessageListenerConcurrently() { + + @Override + public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, + ConsumeConcurrentlyContext context) { + System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs); + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + } }); /* @@ -9005,6 +9112,20 @@ git commit -m "revert OLDER_COMMIT to NEWER_COMMIT"RocketMQ- 分享记录一下一个 scp 操作方法 -/2022/02/06/%E5%88%86%E4%BA%AB%E8%AE%B0%E5%BD%95%E4%B8%80%E4%B8%8B%E4%B8%80%E4%B8%AA-scp-%E6%93%8D%E4%BD%9C%E6%96%B9%E6%B3%95/ -scp 是个在服务器之间拷贝文件的一个常用命令,有时候有个场景是比如我们需要拷贝一些带有共同前缀的文件,但是有一个问题是比如我们有使用 zsh 的话,会出现一个报错, - -
-zsh: no matches found: root@100.100.100.100://root/prefix*这里就比较奇怪了,这个前缀的文件肯定是有的,这里其实是由于 zsh 会对
-*进行展开,这个可以在例如ls命令在使用中就可以发现zsh有这个特性
需要使用双引号或单引号将路径包起来或者在*之前加反斜杠\来阻止对*展开和转义
-scp root@100.100.100.100://root/prefix* .通过使用双引号
-"进行转义
-scp root@100.100.100.100:"//root/prefix*" .或者可以将 shell 从 zsh 切换成 bash
-]]>- -shell -小技巧 -- -scp -聊一下 RocketMQ 的消息存储二 /2021/09/12/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8%E4%BA%8C/ @@ -10344,12 +10446,12 @@ git commit -m "revert OLDER_COMMIT to NEWER_COMMIT"生活运动 东京奥运会 -举重 -射击 +乒乓球 +跳水 - -聊在东京奥运会闭幕式这天 -/2021/08/08/%E8%81%8A%E5%9C%A8%E4%B8%9C%E4%BA%AC%E5%A5%A5%E8%BF%90%E4%BC%9A%E9%97%AD%E5%B9%95%E5%BC%8F%E8%BF%99%E5%A4%A9/ -这届奥运会有可能是我除了 08 年之外关注度最高的一届奥运会,原因可能是因为最近也没什么电影综艺啥的比较好看,前面看跑男倒还行,不是说多好,也就图一乐,最开始看向往的生活觉得也挺不错的,后面变成了统一来了就看黄磊做饭,然后夸黄磊做饭好吃,然后无聊的说这种生活多么多么美好,单调无聊,差不多弃了,这里面还包括大华不在了,大华其实个人还是有点呱噪的,但是挺能搞气氛,并且也有才华,彭彭跟子枫人是不讨厌,但是撑不起来,所以也导致了前面说的结果,都变成了黄磊彩虹屁现场,虽然偶尔怀疑他是否做得好吃,但是整体还是承认的,可对于一个这么多季了的综艺来说,这样也有点单调了。 - -还有奥运会像乒乓球,篮球,跳水这几个都是比较喜欢的项目,篮球🏀是从初中开始就也有在自己在玩的,虽然因为身高啊体质基本没什么天赋,但也算是热爱驱动,差不多到了大学因为比较懒才放下了,初中高中还是有很多时间花在上面,不像别人经常打球跑跑跳跳还能长高,我反而一直都没长个子,也因为这个其实蛮遗憾的,后面想想可能是初中的时候远走他乡去住宿读初中,伙食营养跟不上导致的,可能也是自己的一厢情愿吧,总觉得应该还能再长点个,这一点以后我自己的小孩我应该会特别注意这段时间他/她的营养摄入了;然后像乒乓球🏓的话其实小时候是比较讨厌的,因为家里人,父母都没有这类爱好习惯,我也完全不会,但是小学那会班里的“恶霸”就以公平之名要我们男生每个人都排队打几个,我这种不会的反而又要被嘲笑,这个小时候的阴影让我有了比较不好的印象,对它🏓的改观是在工作以后,前司跟一个同样不会的同事经常在饭点会打打,而且那会因为这个其实身体得到了锻炼,感觉是个不错的健身方式,然后又是中国的优势项目,小时候跟着我爸看孔令辉,那时候完全不懂,印象就觉得老瓦很牛,后面其实也没那么关注,上一届好像看了马龙的比赛;跳水也是中国的优势项目,而且也比较简单,不是说真的很简单,就是我们外行观众看着就看看水花大小图一乐。
-这次的观赛过程其实主要还是在乒乓球上面,现在都有点怪我的乌鸦嘴,混双我一直就不太放心(关我什么事,我也不专业),然后一直觉得混双是不是不太稳,结果那天看的时候也是因为央视一套跟五套都没放,我家的有线电视又是没有五加体育,然后用电脑投屏就很卡,看得也很不爽,同时那天因为看的时候已经是 2:0还是再后面点了,一方面是不懂每队只有一次暂停,另一方面不知道已经用过暂停了,所以就特别怀疑马林是不是只会无脑鼓掌,感觉作为教练,并且是前冠军,应该也能在擦汗间隙,或者局间休息调整的时候多给些战略战术的指导,类似于后面男团小胖打奥恰洛夫,像解说都看出来了,其实奥恰那会的反手特别顺,打得特别凶,那就不能让他能特别顺手的上反手位,这当然是外行比较粗浅的看法,在混双过程中其实除了这个,还有让人很不爽的就是我们的许昕跟刘诗雯有种拿不出破釜沉舟的勇气的感觉,在气势上完全被对面两位日本乒乓球最讨厌的两位对手压制着,我都要输了,我就每一颗都要不让你好过,因为真的不是说没有实力,对面水谷隼也不是多么多么强的,可能上一届男团许昕输给他还留着阴影,但是以许昕 19 年男单世界第一的实力,目前也排在世界前三,输一场不应该成为这种阻力,有一些失误也很可惜,后面孙颖莎真的打得很解气,第二局一度以为又要被翻盘了,结果来了个大逆转,女团的时候也是,感觉在心态上孙颖莎还是很值得肯定的,少年老成这个词很适合,看其他的视频也觉得莎莎萌萌哒,陈梦总感觉还欠一点王者霸气,王曼昱还是可以的,反手很凶,我觉得其实这一届日本女乒就是打得非常凶,即使像平野这种看着很弱的妹子,打的球可一点都不弱,也是这种凶狠的打法,有点要压制中国的感觉,这方面我觉得是需要改善的,打这种要不就是实力上的完全碾压,要不就是我实力虽然比较没强多少,但是你狠我打得比你还狠,越保守越要输,我不太成熟的想法是这样的,还有就是面对逆境,这个就要说到男队的了,樊振东跟马龙在半决赛的时候,特别是男团的第二盘,樊振东打奥恰很好地表现了这个心态,当然樊振东我不是特别了解,据说他是比较善于打相持,比较善于焦灼的情况,不过整体看下来樊振东还是有一些欠缺,就是面对情况的快速转变应对,这一点也是马龙特别强的,虽然看起来马龙真的是年纪大了点,没有 16 年那会满头发胶,油光锃亮的大背头和满脸胶原蛋白的意气风发,大范围运动能力也弱了一点,但是经验和能力的全面性也让他最终能再次站上巅峰,还是非常佩服的,这里提一下张继科,虽然可能天赋上是张继科更强点,但是男乒一直都是有强者出现,能为国家队付出这么多并且一直坚持的可不是人人都可以,即使现在同台竞技马龙打不过张继科我还是更喜欢马龙。再来说说我们的对手,主要分三部分,德国男乒,里面有波尔(我刚听到的时候在想怎么又出来个叫波尔的,是不是像举重的石智勇一样,又来一个同名的,结果是同一个,已经四十岁了),这真是个让人敬佩的对手,实力强,经验丰富,虽然男单有点可惜,但是帮助男团获得银牌,真的是起到了定海神针的作用;奥恰洛夫,以前完全不认识,或者说看过也忘了,这次是真的有点意外,竟然有这么个马龙护法,其实他也坦言非常想赢一次马龙,并且在半决赛也非常接近赢得比赛,是个实力非常强的对手,就是男团半决赛输给张本智和有点可惜,有点被打蒙的感觉,佛朗西斯卡的话也是实力不错的选手,就是可能被奥恰跟波尔的光芒掩盖了,跟波尔在男团第一盘男双的比赛中打败日本那对男双也是非常给力的,说实话,最后打国乒的时候的确是国乒实力更胜一筹,但是即使德国赢了我也是充满尊敬,拼的就是硬实力,就像第二盘奥恰打樊振东,反手是真的很强,反过来看奥恰可能也不是很善于快速调整,樊振东打出来自己的节奏,主攻奥恰的中路,他好像没什么好办法解决。再来说我最讨厌的日本,嗯,小日本,张本智和、水谷隼、伊藤美诚,一一评价下(我是外行,绝对主观评价),张本智和,父母也是中国人,原来叫张智和,改日本籍后加了个本,被微博网友笑称日本尖叫鸡,男单输给了斯洛文尼亚选手,男团里是赢了两场,但是在我看来其实实力上可能比不上全力的奥恰,主要是特别能叫,会干扰对手,如果觉得这种也是种能力我也无话可说,要是有那种吼声能直接把对手震聋的,都不需要打比赛了,我简单记了下,赢一颗球,他要叫八声,用 LD 的话来说烦都烦死了,心态是在面对一些困境顺境的应对调整适应能力,而不是对这种噪音的适应能力,至少我是这么看的,所以我很期待樊振东能好好地虐虐他,因为其他像林昀儒真的是非常优秀的新选手,所谓的国乒克星估计也是小日本自己说说的,国乒其实有很多对手,马龙跟樊振东在男单半决赛碰到的这两个几乎都差点把他们掀翻了,所以还是练好自己的实力再来吹吧,免得打脸;水谷隼的话真的是长相就是特别地讨厌,还搞出那套不打比赛的姿态,男团里被波尔干掉就是很好的例子,波尔虽然真的很强,但毕竟 40 岁了,跟伊藤美诚一起说了吧,伊藤实力说实话是有的,混双中很大一部分的赢面来自于她,刘诗雯做了手术状态不好,许昕失误稍多,但是这种赢球了就感觉我赢了你一辈子一场没输的感觉,还有那种不知道怎么形容的笑,实力强的正常打比赛的我都佩服,像女团决赛里,平野跟石川佳纯的打法其实也很凶狠,但是都是正常的比赛,即使中国队两位实力不济输了也很正常,这种就真的需要像孙颖莎这样的小魔王无视各种魔法攻击,无视你各种花里胡哨的打法的人好好教训一下,混双输了以后了解了下她,感觉实力真的不错,是个大威胁,但是其实我们孙颖莎也是经历了九个月的继续成长,像张怡宁也评价了她,可能后面就没什么空间了,当然如果由张怡宁来打她就更适合了,净整这些有的没的,就打得你没脾气。
-乒乓球的说的有点多,就分篇说了,第一篇先到这。
+聊在东京奥运会闭幕式这天-二 +/2021/08/19/%E8%81%8A%E5%9C%A8%E4%B8%9C%E4%BA%AC%E5%A5%A5%E8%BF%90%E4%BC%9A%E9%97%AD%E5%B9%95%E5%BC%8F%E8%BF%99%E5%A4%A9-%E4%BA%8C/ +前面主要还是说了乒乓球的,因为整体还是乒乓球的比赛赛程比较长,比较激烈,扣人心弦,记得那会在公司没法看视频直播,就偶尔看看奥运会官网的比分,还几场马龙樊振东,陈梦被赢了一局就吓尿了,已经被混双那场留下了阴影,其实后面去看看16 年的比赛什么的,中国队虽然拿了这么多冠军,但是自改成 11 分制以来,其实都没办法那么完全彻底地碾压,而且像张继科,樊振东,陈梦都多少有些慢热,现在看来是马龙比较全面,不过看过了马龙,刘国梁,许昕等的一些过往经历,都是起起伏伏,即使是张怡宁这样的大魔王,也经历过逢王楠不赢的阶段,心态无法调整好。 + 其实最开始是举重项目,侯志慧是女子 49 公斤级的冠军,这场比赛是全场都看,其实看中国队的举重比赛跟跳水有点像,每一轮都需要到最后才能等到中国队,跳水其实每轮都有,举重会按照自己报的试举重量进行排名,重量大的会在后面举,抓举和挺举各三次试举机会,有时候会看着比较焦虑,一直等不来,怕一上来就没试举成功,而且中国队一般试举重量就是很大的,容易一次试举不成功就马上下一次,连着举其实压力会非常大,说实话真的是外行看热闹,每次都是多懂一点点,这次由于实在是比较无聊,所以看的会比较专心点,对于对应的规则知识点也会多了解一点,同时对于举重,没想到我们国家的这些运动员有这么强,最后八块金牌拿了七块,有一块拿到银牌也是有点因为教练的策略问题,这里其实也稍微知道一点,因为报上去的试举重量是谁小谁先举,并且我们国家都是实力非常强的,所以都会报大一些,并且如果这个项目有实力相近的选手,会比竞对多报一公斤,这样子如果前面竞争对手没举成功,我们把握就很大了,最坏的情况即使对手试举成功了,我们还有机会搏一把,比如谌利军这样的,只是说说感想,举重运动员真的是个比较单纯的群体,而且训练是非常痛苦枯燥的,非常容易受伤,像挺举就有点会压迫呼吸通道,看到好几个都是脸憋得通红,甚至直接因为压迫气道而没法完成后面的挺举,像之前 16 年的举重比赛,有个运动员没成功夺冠就非常愧疚地哭着说对不起祖国,没有获得冠军,这是怎么样的一种歉疚,怎么样的一种纯粹的感情呢,相对应地来说,我又要举男足,男篮的例子了,很多人在那嘲笑我这样对男足男篮愤愤不平的人,说可能我这样的人都没交个税(从缴纳个税的数量比例来算有可能),只是这里有两个打脸的事情,我足额缴纳个税,接近 20%的薪资都缴了个税,并且我买的所有东西都缴了增值税,如果让我这样缴纳了个税,缴纳了增值税的有个人的投票权,我一定会投票不让男足男篮使用我缴纳我的税金,用我们的缴纳的税,打出这么烂的表现,想乒乓球混双,拿个亚军都会被喷,那可是世界第二了,而且是就输了那么一场,足球篮球呢,我觉得是一方面成绩差,因为比赛真的有状态跟心态的影响,偶尔有一场失误非常正常,NBA 被黑八的有这么多强队,但是如果像男足男篮,成绩是越来越差,用范志毅的话来说就是脸都不要了,还有就是精气神,要在比赛中打出胜负欲,保持这种争胜心,才有机会再进步,前火箭队主教练鲁迪·汤姆贾诺维奇的话,“永远不要低估冠军的决心”,即使我现在打不过你,我会在下一次,下下次打败你,竞技体育永远要有这种精神,可以接受一时的失败,但是要保持永远争胜的心。
+第一块金牌是杨倩拿下的,中国队拿奥运会首金也是有政治任务的,而恰恰杨倩这个金牌也有点碰巧是对手最后一枪失误了,当然竞技体育,特别是射击,真的是容不得一点点失误,像前面几届的美国神通埃蒙斯,失之毫厘差之千里,但是这个具体评价就比较少,唯一一点让我比较出戏的就是杨倩真的非常像王刚的徒弟漆二娃,哈哈,微博上也有挺多人觉得像,射击还是个比较可以接受年纪稍大的运动员,需要经验和稳定性,相对来说爆发力体力稍好一点,像庞伟这样的,混合团体10米气手枪金牌,36 岁可能其他项目已经是年龄很大了,不过前面说的举重的吕小军军神也是年纪蛮大了,但是非常强,而且在油管上简直就是个神,相对来说射击是关注比较少,杨倩的也只是看了后面拿到冠军这个结果,有些因为时间或者电视上没放,但是成绩还是不错的,没多少喷点。
+第二篇先到这,纯主观,轻喷。
]]>生活 @@ -10379,8 +10481,8 @@ git commit -m "revert OLDER_COMMIT to NEWER_COMMIT"Mysql 字符编码和排序规则这个一直是属于一知半解的状态,知道 utf8 跟 utf8mb4 的区别主要是能不能支持 emoji,但是具体后面配置的排序规则是用来干嘛,或者有什么区别,应该使用哪个,所以在 stackoverflow 上找了下,有两个比较不错的解答,就搬过来并且配合机翻做了点修改
-原文
For those people still arriving at this question in 2020 or later, there are newer options that may be better than both of these. For example,
-utf8mb4_0900_ai_ci.All these collations are for the UTF-8 character encoding. The differences are in how text is sorted and compared.
-
-_unicode_ciand_general_ciare two different sets of rules for sorting and comparing text according to the way we expect. Newer versions of MySQL introduce new sets of rules, too, such as_0900_ai_cifor equivalent rules based on Unicode 9.0 - and with no equivalent_general_civariant. People reading this now should probably use one of these newer collations instead of either_unicode_cior_general_ci. The description of those older collations below is provided for interest only.MySQL is currently transitioning away from an older, flawed UTF-8 implementation. For now, you need to use utf8mb4 instead of utf8 for the character encoding part, to ensure you are getting the fixed version. The flawed version remains for backward compatibility, though it is being deprecated.
-Key differences
-
-utf8mb4_unicode_ciis based on the official Unicode rules for universal sorting and comparison, which sorts accurately in a wide range of languages.
-utf8mb4_general_ciis a simplified set of sorting rules which aims to do as well as it can while taking many short-cuts designed to improve speed. It does not follow the Unicode rules and will result in undesirable sorting or comparison in some situations, such as when using particular languages or characters.On modern servers, this performance boost will be all but negligible. It was devised in a time when servers had a tiny fraction of the CPU performance of today’s computers.
-Benefits of
-utf8mb4_unicode_cioverutf8mb4_general_ciutf8mb4_unicode_ci, which uses the Unicode rules for sorting and comparison, employs a fairly complex algorithm for correct sorting in a wide range of languages and when using a wide range of special characters. These rules need to take into account language-specific conventions; not everybody sorts their characters in what we would call ‘alphabetical order’.
-As far as Latin (ie “European”) languages go, there is not much difference between the Unicode sorting and the simplified
-utf8mb4_general_cisorting in MySQL, but there are still a few differences:For examples, the Unicode collation sorts “ß” like “ss”, and “Œ” like “OE” as people using those characters would normally want, whereas
-utf8mb4_general_cisorts them as single characters (presumably like “s” and “e” respectively).Some Unicode characters are defined as ignorable, which means they shouldn’t count toward the sort order and the comparison should move on to the next character instead.
-utf8mb4_unicode_cihandles these properly.In non-latin languages, such as Asian languages or languages with different alphabets, there may be a lot more differences between Unicode sorting and the simplified
-utf8mb4_general_cisorting. The suitability ofutf8mb4_general_ciwill depend heavily on the language used. For some languages, it’ll be quite inadequate.What should you use?
-There is almost certainly no reason to use
-utf8mb4_general_cianymore, as we have left behind the point where CPU speed is low enough that the performance difference would be important. Your database will almost certainly be limited by other bottlenecks than this.In the past, some people recommended to use
-utf8mb4_general_ciexcept when accurate sorting was going to be important enough to justify the performance cost. Today, that performance cost has all but disappeared, and developers are treating internationalization more seriously.There’s an argument to be made that if speed is more important to you than accuracy, you may as well not do any sorting at all. It’s trivial to make an algorithm faster if you do not need it to be accurate. So,
-utf8mb4_general_ciis a compromise that’s probably not needed for speed reasons and probably also not suitable for accuracy reasons.One other thing I’ll add is that even if you know your application only supports the English language, it may still need to deal with people’s names, which can often contain characters used in other languages in which it is just as important to sort correctly. Using the Unicode rules for everything helps add peace of mind that the very smart Unicode people have worked very hard to make sorting work properly.
-What the parts mean
-Firstly, ci is for case-insensitive sorting and comparison. This means it’s suitable for textual data, and case is not important. The other types of collation are cs (case-sensitive) for textual data where case is important, and bin, for where the encoding needs to match, bit for bit, which is suitable for fields which are really encoded binary data (including, for example, Base64). Case-sensitive sorting leads to some weird results and case-sensitive comparison can result in duplicate values differing only in letter case, so case-sensitive collations are falling out of favor for textual data - if case is significant to you, then otherwise ignorable punctuation and so on is probably also significant, and a binary collation might be more appropriate.
-Next, unicode or general refers to the specific sorting and comparison rules - in particular, the way text is normalized or compared. There are many different sets of rules for the utf8mb4 character encoding, with unicode and general being two that attempt to work well in all possible languages rather than one specific one. The differences between these two sets of rules are the subject of this answer. Note that unicode uses rules from Unicode 4.0. Recent versions of MySQL add the rulesets unicode_520 using rules from Unicode 5.2, and 0900 (dropping the “unicode_” part) using rules from Unicode 9.0.
-And lastly, utf8mb4 is of course the character encoding used internally. In this answer I’m talking only about Unicode based encodings.
-翻译
对于那些在 2020 年或之后仍会遇到这个问题的人,有可能比这两个更好的新选项。例如,
-utf8mb4_0900_ai_ci。所有这些排序规则都用于 UTF-8 字符编码。不同之处在于文本的排序和比较方式。
-
-_unicode_ci和_general_ci是两组不同的规则,用于按照我们期望的方式对文本进行排序和比较。较新版本的 MySQL 也引入了新的规则集,例如_0900_ai_ci用于基于 Unicode 9.0 的等效规则 - 并且没有等效的_general_ci变体。现在阅读本文的人可能应该使用这些较新的排序规则之一,而不是_unicode_ci或_general_ci。下面对那些较旧的排序规则的描述仅供参考。MySQL 目前正在从旧的、有缺陷的 UTF-8 实现过渡。现在,您需要使用
-utf8mb4而不是utf8作为字符编码部分,以确保您获得的是固定版本。有缺陷的版本仍然是为了向后兼容,尽管它已被弃用。主要区别
-
-utf8mb4_unicode_ci基于官方 Unicode 规则进行通用排序和比较,可在多种语言中准确排序。
-utf8mb4_general_ci是一组简化的排序规则,旨在尽其所能,同时采用许多旨在提高速度的捷径。它不遵循 Unicode 规则,并且在某些情况下会导致不希望的排序或比较,例如在使用特定语言或字符时。在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器的 CPU 性能只有当今计算机的一小部分时设计的。
-
-utf8mb4_unicode_ci相对于utf8mb4_general_ci的优势
-utf8mb4_unicode_ci使用 Unicode 规则进行排序和比较,采用相当复杂的算法在多种语言中以及在使用多种特殊字符时进行正确排序。这些规则需要考虑特定语言的约定;不是每个人都按照我们所说的“字母顺序”对他们的字符进行排序。就拉丁语(即“欧洲”)语言而言,Unicode 排序和 MySQL 中简化的
-utf8mb4_general_ci排序没有太大区别,但仍有一些区别:例如,Unicode 排序规则将“ß”排序为“ss”,将“Œ”排序为“OE”,因为使用这些字符的人通常需要这些字符,而
-utf8mb4_general_ci将它们排序为单个字符(大概分别像“s”和“e” )。一些 Unicode 字符被定义为可忽略,这意味着它们不应该计入排序顺序,并且比较应该转到下一个字符。
-utf8mb4_unicode_ci正确处理这些。在非拉丁语言中,例如亚洲语言或具有不同字母的语言,Unicode 排序和简化的
-utf8mb4_general_ci排序之间可能存在更多差异。utf8mb4_general_ci的适用性在很大程度上取决于所使用的语言。对于某些语言,这将是非常不充分的。你应该用什么?
-几乎可以肯定没有理由再使用
-utf8mb4_general_ci,因为我们已经将 CPU 速度低到会严重影响性能表现的时代远抛在脑后了。您的数据库几乎肯定会受到除此之外的其他瓶颈的限制。过去,有些人建议使用
-utf8mb4_general_ci,除非准确排序足够重要以证明性能成本是合理的。如今,这种性能成本几乎消失了,开发人员正在更加认真地对待国际化。有一个论点是,如果速度对您来说比准确性更重要,那么您可能根本不进行任何排序。如果您不需要准确的算法,那么使算法更快是微不足道的。因此,
-utf8mb4_general_ci是一种折衷方案,出于速度原因可能不需要,也可能出于准确性原因也不适合。我要补充的另一件事是,即使您知道您的应用程序仅支持英语,它可能仍需要处理人名,这些人名通常包含其他语言中使用的字符,在这些语言中正确排序同样重要.对所有事情都使用 Unicode 规则有助于让您更加安心,因为非常聪明的 Unicode 人员已经非常努力地工作以使排序正常工作。
-其余各个部分是什么意思
-首先,
-ci用于不区分大小写的排序和比较。这意味着它适用于文本数据,大小写并不重要。其他类型的排序规则是cs(区分大小写),用于区分大小写的文本数据,以及bin,用于编码需要匹配的地方,逐位匹配,适用于真正编码二进制数据的字段(包括,用于例如,Base64)。区分大小写的排序会导致一些奇怪的结果,区分大小写的比较可能会导致重复值仅在字母大小写上有所不同,因此区分大小写的排序规则对文本数据不受欢迎 - 如果大小写对您很重要,那么标点符号就可以忽略等等可能也很重要,二进制排序规则可能更合适。接下来,unicode 或general 指的是具体的排序和比较规则——特别是文本被规范化或比较的方式。 utf8mb4 字符编码有许多不同的规则集,其中 unicode 和 general 是两种,它们试图在所有可能的语言中都很好地工作,而不是在一种特定的语言中。这两组规则之间的差异是此答案的主题。请注意,unicode 使用 Unicode 4.0 中的规则。 MySQL 的最新版本使用 Unicode 5.2 的规则添加规则集 unicode_520,使用 Unicode 9.0 的规则添加 0900(删除“unicode_”部分)。
-最后,utf8mb4 当然是内部使用的字符编码。在这个答案中,我只谈论基于 Unicode 的编码。
-utf8 和 utf8mb4 编码有什么区别
原文
UTF-8is a variable-length encoding. In the case of UTF-8, this means that storing one code point requires one to four bytes. However, MySQL’s encoding called “utf8” (alias of “utf8mb3”) only stores a maximum of three bytes per code point.
-So the character set “utf8”/“utf8mb3” cannot store all Unicode code points: it only supports the range 0x000 to 0xFFFF, which is called the “Basic Multilingual Plane“. See also Comparison of Unicode encodings.
-This is what (a previous version of the same page at)the MySQL documentationhas to say about it:
--
-The character set named utf8[/utf8mb3] uses a maximum of three bytes per character and contains only BMP characters. As of MySQL 5.5.3, the utf8mb4 character set uses a maximum of four bytes per character supports supplemental characters:
--
-
- For a BMP character, utf8[/utf8mb3] and utf8mb4 have identical storage characteristics: same code values, same encoding, same length. -
- For a supplementary character, utf8[/utf8mb3] cannot store the character at all, while utf8mb4 requires four bytes to store it. Since utf8[/utf8mb3] cannot store the character at all, you do not have any supplementary characters in utf8[/utf8mb3] columns and you need not worry about converting characters or losing data when upgrading utf8[/utf8mb3] data from older versions of MySQL. -
So if you want your column to support storing characters lying outside the BMP (and you usually want to), such as emoji, use “utf8mb4”. See also What are the most common non-BMP Unicode characters in actual use?.
-译文
UTF-8 是一种可变长度编码。对于 UTF-8,这意味着存储一个代码点需要一到四个字节。但是,MySQL 的编码称为“utf8”(“utf8mb3”的别名)每个代码点最多只能存储三个字节。
-所以字符集“utf8”/“utf8mb3”不能存储所有的Unicode码位:它只支持0x000到0xFFFF的范围,被称为“基本多语言平面”。另请参阅 Unicode 编码比较。
-这就是(同一页面的先前版本)MySQL 文档 不得不说的:
--
-名为 utf8[/utf8mb3] 的字符集每个字符最多使用三个字节,并且仅包含 BMP 字符。从 MySQL 5.5.3 开始,utf8mb4 字符集每个字符最多使用四个字节,支持补充字符:
--
-
- 对于 BMP 字符,utf8[/utf8mb3] 和 utf8mb4 具有相同的存储特性:相同的代码值、相同的编码、相同的长度。 -
- 对于补充字符,utf8[/utf8mb3] 根本无法存储该字符,而 utf8mb4 需要四个字节来存储它。由于 utf8[/utf8mb3] 根本无法存储字符,因此您在 utf8[/utf8mb3] 列中没有任何补充字符,您不必担心从旧版本升级 utf8[/utf8mb3] 数据时转换字符或丢失数据mysql。 -
因此,如果您希望您的列支持存储位于 BMP 之外的字符(并且您通常希望这样做),例如 emoji,请使用“utf8mb4”。另请参阅
- -]]>- -Mysql -- -mysql -字符集 -编码 -utf8 -utf8mb4 -utf8mb4_0900_ai_ci -utf8mb4_unicode_ci -utf8mb4_general_ci -- 给小电驴上牌 -/2022/03/20/%E7%BB%99%E5%B0%8F%E7%94%B5%E9%A9%B4%E4%B8%8A%E7%89%8C/ -三八节活动的时候下决心买了个小电驴,主要是上下班路上现在通勤条件越来越恶劣了,之前都是觉得坐公交就行了,实际路程就比较短,但是现在或者说大概是年前那两个月差不多就开始了,基本是堵一路,个人感觉是天目山路那边在修地铁,而且蚂蚁的几个空间都在那,上班的时间点都差不多,前一个修地铁感觉挺久了,机动车保有量也越来越多,总体是古墩路就越来越堵,还有个原因就是早上上班的点共享单车都被骑走了,有时候整整走一路都没一辆,有时候孤零零地有一辆基本都是破的;走路其实也是一种选择,但是因为要赶着上班,走得太慢就要很久,可能要 45 分钟这样,走得比较快就一身汗挺难受的。所以考虑自行车和电动车,这里还有一点就是不管是乘公交还是骑共享单车,其实都要从楼下走出去蛮远,公司回来也是,也就是这种通勤方式在准备阶段就花了比较多时间,比如总的从下班到到家的时间是半小时,可能在骑共享单车和公交车上的时间都不到十分钟,就比较难受。觉得这种比例太浪费时间,如果能有这种比较点对点的方式,估计能省时省力不少,前面说的骑共享单车的方式其实在之前是比较可行的,但是后来越来越少车,基本都是每周的前几天,周一到周三都是没有车,走路到公司再冷的天都是走出一身的汗,下雨天就更难受,本来下雨天应该是优先选择坐公交,但是一般下雨天堵车会更严重,而且车子到我上车的那个站,下雨天就挤得不行,总体说下来感觉事情都不打,但是几年下来,还是会挺不爽的。 - -电驴看的比较草率,主要是考虑续航,然后锂电池外加 48v 和 24AH,这样一般来讲还是价格比较高的,只是原来没预料到这个限速,以为现在的车子都比较快,但是现在的新国标车子都是 25km/h 的限速,然后 15km/h 都是会要提醒,虽然说有一些特殊的解除限速的方法,但是解了也就 35km/h ,差距不是特别大,而且现在的车子都是比较小,也不太能载东西,特别是上下班路程也不远的情况下,其实不是那么需要速度,就像我朋友说的,可能骑车的时间还不如等红绿灯多,所以就还好,也不打算解除限速,只是品牌上也仔细看,后来选了绿源,目前大部分还是雅迪,爱玛,台羚,绿源,小牛等,路上看的话还是雅迪比较多,不过价格也比较贵一点,还有就是小牛了,是比较新兴的品牌,手机 App 什么的做得比较好,而且也比较贵,最后以相对比较便宜的价格买了个锂电 48V24AH 的小车子,后来发现还是有点不方便的点就是没有比较大的筐,也不好装,这样就是下雨天雨衣什么的比较不方便放。
-聊回来主题上牌这个事情,这个事情也是颇费心力,提车的时候店里的让我跟他早上一起去,但是因为不确定时间,也比较远就没跟着去,因为我是线上买的,线下自提,线下的店可能没啥利润可以拿,就不肯帮忙代上牌,朋友说在线下店里买是可以代上的,自己上牌过程也比较曲折,一开始是头盔没到,然后是等开发票,主要的东西就是需要骑着车子去车管所,不能只自己去,然后需要预约,附近比较近的都是提前一周就预约完了号了,要提前在支付宝上进行预约,比较空的就是店里推荐的景区大队,但是随之而来就是比较蛋疼的,这个景区大队太远了,看下骑车距离有十几公里,所以就有点拖延症,但是总归要上的,不然一直不能开是白买了,上牌的材料主要是车辆合格证,发票,然后车子上的浙品码,在车架上和电池上,然后车架号什么的都要跟合格证上完全对应,整体车子要跟合格证上一毛一样,如果有额外的反光镜,后面副座都需要拆掉,脚踏板要装上,到了那其实还比较顺利,就是十几公里外加那天比较冷,吹得头疼。
-]]>- -生活 -- -生活 - -聊聊 Java 中绕不开的 Synchronized 关键字-二 /2021/06/27/%E8%81%8A%E8%81%8A-Java-%E4%B8%AD%E7%BB%95%E4%B8%8D%E5%BC%80%E7%9A%84-Synchronized-%E5%85%B3%E9%94%AE%E5%AD%97-%E4%BA%8C/ @@ -11068,175 +11068,6 @@ public Result invoke(final Invocation invocation) throws RpcException {然后呢就是为啥一些书或者
]]>effective java中写了equals跟hashCode要一起重写,这里涉及到当对象作为HashMap的key的时候
首先HashMap会使用hashCode去判断是否在同一个槽里,然后在通过equals去判断是否是同一个key,是的话就替换,不是的话就链表接下去,如果不重写hashCode的话,默认的object的hashCode是native方法,根据对象的地址生成的,这样其实对象的值相同的话,因为地址不同,HashMap也会出现异常,所以需要重写,同时也需要重写equals方法,才能确认是同一个key,而不是落在同一个槽的不同key.- 聊聊 Java 中绕不开的 Synchronized 关键字 -/2021/06/20/%E8%81%8A%E8%81%8A-Java-%E4%B8%AD%E7%BB%95%E4%B8%8D%E5%BC%80%E7%9A%84-Synchronized-%E5%85%B3%E9%94%AE%E5%AD%97/ -Synchronized 关键字在 Java 的并发体系里也是非常重要的一个内容,首先比较常规的是知道它使用的方式,可以锁对象,可以锁代码块,也可以锁方法,看一个简单的 demo - -
- -public class SynchronizedDemo { - - public static void main(String[] args) { - SynchronizedDemo synchronizedDemo = new SynchronizedDemo(); - synchronizedDemo.lockMethod(); - } - - public synchronized void lockMethod() { - System.out.println("here i'm locked"); - } - - public void lockSynchronizedDemo() { - synchronized (this) { - System.out.println("here lock class"); - } - } -}然后来查看反编译结果,其实代码(日光)之下并无新事,即使是完全不懂的也可以通过一些词义看出一些意义
-
- -Last modified 2021年6月20日; size 729 bytes - MD5 checksum dd9c529863bd7ff839a95481db578ad9 - Compiled from "SynchronizedDemo.java" -public class SynchronizedDemo - minor version: 0 - major version: 53 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #2 // SynchronizedDemo - super_class: #9 // java/lang/Object - interfaces: 0, fields: 0, methods: 4, attributes: 1 -Constant pool: - #1 = Methodref #9.#22 // java/lang/Object."<init>":()V - #2 = Class #23 // SynchronizedDemo - #3 = Methodref #2.#22 // SynchronizedDemo."<init>":()V - #4 = Methodref #2.#24 // SynchronizedDemo.lockMethod:()V - #5 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream; - #6 = String #27 // here i\'m locked - #7 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V - #8 = String #30 // here lock class - #9 = Class #31 // java/lang/Object - #10 = Utf8 <init> - #11 = Utf8 ()V - #12 = Utf8 Code - #13 = Utf8 LineNumberTable - #14 = Utf8 main - #15 = Utf8 ([Ljava/lang/String;)V - #16 = Utf8 lockMethod - #17 = Utf8 lockSynchronizedDemo - #18 = Utf8 StackMapTable - #19 = Class #32 // java/lang/Throwable - #20 = Utf8 SourceFile - #21 = Utf8 SynchronizedDemo.java - #22 = NameAndType #10:#11 // "<init>":()V - #23 = Utf8 SynchronizedDemo - #24 = NameAndType #16:#11 // lockMethod:()V - #25 = Class #33 // java/lang/System - #26 = NameAndType #34:#35 // out:Ljava/io/PrintStream; - #27 = Utf8 here i\'m locked - #28 = Class #36 // java/io/PrintStream - #29 = NameAndType #37:#38 // println:(Ljava/lang/String;)V - #30 = Utf8 here lock class - #31 = Utf8 java/lang/Object - #32 = Utf8 java/lang/Throwable - #33 = Utf8 java/lang/System - #34 = Utf8 out - #35 = Utf8 Ljava/io/PrintStream; - #36 = Utf8 java/io/PrintStream - #37 = Utf8 println - #38 = Utf8 (Ljava/lang/String;)V -{ - public SynchronizedDemo(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #1 // Method java/lang/Object."<init>":()V - 4: return - LineNumberTable: - line 5: 0 - - public static void main(java.lang.String[]); - descriptor: ([Ljava/lang/String;)V - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=2, locals=2, args_size=1 - 0: new #2 // class SynchronizedDemo - 3: dup - 4: invokespecial #3 // Method "<init>":()V - 7: astore_1 - 8: aload_1 - 9: invokevirtual #4 // Method lockMethod:()V - 12: return - LineNumberTable: - line 8: 0 - line 9: 8 - line 10: 12 - - public synchronized void lockMethod(); - descriptor: ()V - flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED - Code: - stack=2, locals=1, args_size=1 - 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; - 3: ldc #6 // String here i\'m locked - 5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V - 8: return - LineNumberTable: - line 13: 0 - line 14: 8 - - public void lockSynchronizedDemo(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=2, locals=3, args_size=1 - 0: aload_0 - 1: dup - 2: astore_1 - 3: monitorenter - 4: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; - 7: ldc #8 // String here lock class - 9: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V - 12: aload_1 - 13: monitorexit - 14: goto 22 - 17: astore_2 - 18: aload_1 - 19: monitorexit - 20: aload_2 - 21: athrow - 22: return - Exception table: - from to target type - 4 14 17 any - 17 20 17 any - LineNumberTable: - line 17: 0 - line 18: 4 - line 19: 12 - line 20: 22 - StackMapTable: number_of_entries = 2 - frame_type = 255 /* full_frame */ - offset_delta = 17 - locals = [ class SynchronizedDemo, class java/lang/Object ] - stack = [ class java/lang/Throwable ] - frame_type = 250 /* chop */ - offset_delta = 4 -} -SourceFile: "SynchronizedDemo.java"其中
-lockMethod中可以看到是通过ACC_SYNCHRONIZEDflag 来标记是被 synchronized 修饰,前面的 ACC 应该是 access 的意思,并且通过ACC_PUBLIC也可以看出来他们是同一类访问权限关键字来控制的,而修饰类则是通过3: monitorenter和13: monitorexit来控制并发,这个是原来就知道,后来看了下才知道修饰方法是不一样的,但是在前期都比较诟病是 synchronized 的性能,像 monitor 也是通过操作系统的mutex lock互斥锁来实现的,相对是比较重的锁,于是在 JDK 1.6 之后对 synchronized 做了一系列优化,包括偏向锁,轻量级锁,并且包括像 ConcurrentHashMap 这类并发集合都有在使用 synchronized 关键字配合 cas 来做并发保护,jdk 对于 synchronized 的优化主要在于多重状态锁的升级,最初会使用偏向锁,当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)。
-]]>
而当出现线程尝试进入同步块时发现已有偏向锁,并且是其他线程时,会将锁升级成轻量级锁,并且自旋尝试获取锁,如果自旋成功则表示获取轻量级锁成功,否则将会升级成重量级锁进行阻塞,当然这里具体的还很复杂,说的比较浅薄主体还是想将原先的阻塞互斥锁进行轻量化,区分特殊情况进行加锁。- -Java -- -Java -Synchronized -偏向锁 -轻量级锁 -重量级锁 -自旋 -聊聊 Java 的类加载机制一 /2020/11/08/%E8%81%8A%E8%81%8A-Java-%E7%9A%84%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/ @@ -11589,489 +11420,798 @@ public Result invoke(final Invocation invocation) throws RpcException {- +聊聊 Java 自带的那些*逆天*工具 -/2020/08/02/%E8%81%8A%E8%81%8A-Java-%E8%87%AA%E5%B8%A6%E7%9A%84%E9%82%A3%E4%BA%9B%E9%80%86%E5%A4%A9%E5%B7%A5%E5%85%B7/ -原谅我的标题党,其实这些工具的确很厉害,之前其实介绍过一点相关的,是从我一次问题排查的过程中用到的,但是最近又有碰到一次排查问题,发现其实用 idea 直接 dump thread是不现实的,毕竟服务器环境的没法这么操作,那就得用 Java 的那些工具了 -jstack & jps
譬如
-jstack,这个命令其实不能更简单了
看看 help 信息![]()
用-l参数可以打出锁的额外信息,然后后面的 pid 就是进程 id 咯,机智的小伙伴会问了(就你这个小白才问这么蠢的问题🤦♂️),怎么看 Java 应用的进程呢
那就是jps了,命令也很简单,一般直接用jps命令就好了,不过也可以 help 看一下![]()
稍微解释下,-q是只显示进程 id,-m是输出给main 方法的参数,比如我在配置中加给参数![]()
然后用jps -m查看![]()
-v加上小 v 的话就是打印 jvm 参数![]()
还是有点东西,然后就继续介绍 jstack 了,然后我们看看 jstack 出来是啥,为了加点内容我加了个死锁
-public static void main(String[] args) throws InterruptedException { - SpringApplication.run(ThreadDumpDemoApplication.class, args); - ReentrantLock lock1 = new ReentrantLock(); - ReentrantLock lock2 = new ReentrantLock(); - Thread t1 = new Thread() { - @Override - public void run() { - try { - lock1.lock(); - TimeUnit.SECONDS.sleep(1); - lock2.lock(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }; - Thread t2 = new Thread() { - @Override - public void run() { - try { - lock2.lock(); - TimeUnit.SECONDS.sleep(1); - lock1.lock(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }; - t1.setName("mythread1"); - t2.setName("mythread2"); - t1.start(); - t2.start(); - Thread.sleep(10000); - }然后看看出来时怎么样的
-2020-08-02 21:50:32 -Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.201-b09 mixed mode): +聊聊 Java 中绕不开的 Synchronized 关键字 +/2021/06/20/%E8%81%8A%E8%81%8A-Java-%E4%B8%AD%E7%BB%95%E4%B8%8D%E5%BC%80%E7%9A%84-Synchronized-%E5%85%B3%E9%94%AE%E5%AD%97/ +Synchronized 关键字在 Java 的并发体系里也是非常重要的一个内容,首先比较常规的是知道它使用的方式,可以锁对象,可以锁代码块,也可以锁方法,看一个简单的 demo + +
+ +public class SynchronizedDemo { + + public static void main(String[] args) { + SynchronizedDemo synchronizedDemo = new SynchronizedDemo(); + synchronizedDemo.lockMethod(); + } + + public synchronized void lockMethod() { + System.out.println("here i'm locked"); + } + + public void lockSynchronizedDemo() { + synchronized (this) { + System.out.println("here lock class"); + } + } +}然后来查看反编译结果,其实代码(日光)之下并无新事,即使是完全不懂的也可以通过一些词义看出一些意义
+
+ +Last modified 2021年6月20日; size 729 bytes + MD5 checksum dd9c529863bd7ff839a95481db578ad9 + Compiled from "SynchronizedDemo.java" +public class SynchronizedDemo + minor version: 0 + major version: 53 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #2 // SynchronizedDemo + super_class: #9 // java/lang/Object + interfaces: 0, fields: 0, methods: 4, attributes: 1 +Constant pool: + #1 = Methodref #9.#22 // java/lang/Object."<init>":()V + #2 = Class #23 // SynchronizedDemo + #3 = Methodref #2.#22 // SynchronizedDemo."<init>":()V + #4 = Methodref #2.#24 // SynchronizedDemo.lockMethod:()V + #5 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream; + #6 = String #27 // here i\'m locked + #7 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V + #8 = String #30 // here lock class + #9 = Class #31 // java/lang/Object + #10 = Utf8 <init> + #11 = Utf8 ()V + #12 = Utf8 Code + #13 = Utf8 LineNumberTable + #14 = Utf8 main + #15 = Utf8 ([Ljava/lang/String;)V + #16 = Utf8 lockMethod + #17 = Utf8 lockSynchronizedDemo + #18 = Utf8 StackMapTable + #19 = Class #32 // java/lang/Throwable + #20 = Utf8 SourceFile + #21 = Utf8 SynchronizedDemo.java + #22 = NameAndType #10:#11 // "<init>":()V + #23 = Utf8 SynchronizedDemo + #24 = NameAndType #16:#11 // lockMethod:()V + #25 = Class #33 // java/lang/System + #26 = NameAndType #34:#35 // out:Ljava/io/PrintStream; + #27 = Utf8 here i\'m locked + #28 = Class #36 // java/io/PrintStream + #29 = NameAndType #37:#38 // println:(Ljava/lang/String;)V + #30 = Utf8 here lock class + #31 = Utf8 java/lang/Object + #32 = Utf8 java/lang/Throwable + #33 = Utf8 java/lang/System + #34 = Utf8 out + #35 = Utf8 Ljava/io/PrintStream; + #36 = Utf8 java/io/PrintStream + #37 = Utf8 println + #38 = Utf8 (Ljava/lang/String;)V +{ + public SynchronizedDemo(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #1 // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + line 5: 0 + + public static void main(java.lang.String[]); + descriptor: ([Ljava/lang/String;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=1 + 0: new #2 // class SynchronizedDemo + 3: dup + 4: invokespecial #3 // Method "<init>":()V + 7: astore_1 + 8: aload_1 + 9: invokevirtual #4 // Method lockMethod:()V + 12: return + LineNumberTable: + line 8: 0 + line 9: 8 + line 10: 12 + + public synchronized void lockMethod(); + descriptor: ()V + flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; + 3: ldc #6 // String here i\'m locked + 5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V + 8: return + LineNumberTable: + line 13: 0 + line 14: 8 + + public void lockSynchronizedDemo(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=3, args_size=1 + 0: aload_0 + 1: dup + 2: astore_1 + 3: monitorenter + 4: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; + 7: ldc #8 // String here lock class + 9: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V + 12: aload_1 + 13: monitorexit + 14: goto 22 + 17: astore_2 + 18: aload_1 + 19: monitorexit + 20: aload_2 + 21: athrow + 22: return + Exception table: + from to target type + 4 14 17 any + 17 20 17 any + LineNumberTable: + line 17: 0 + line 18: 4 + line 19: 12 + line 20: 22 + StackMapTable: number_of_entries = 2 + frame_type = 255 /* full_frame */ + offset_delta = 17 + locals = [ class SynchronizedDemo, class java/lang/Object ] + stack = [ class java/lang/Throwable ] + frame_type = 250 /* chop */ + offset_delta = 4 +} +SourceFile: "SynchronizedDemo.java"其中
+lockMethod中可以看到是通过ACC_SYNCHRONIZEDflag 来标记是被 synchronized 修饰,前面的 ACC 应该是 access 的意思,并且通过ACC_PUBLIC也可以看出来他们是同一类访问权限关键字来控制的,而修饰类则是通过3: monitorenter和13: monitorexit来控制并发,这个是原来就知道,后来看了下才知道修饰方法是不一样的,但是在前期都比较诟病是 synchronized 的性能,像 monitor 也是通过操作系统的mutex lock互斥锁来实现的,相对是比较重的锁,于是在 JDK 1.6 之后对 synchronized 做了一系列优化,包括偏向锁,轻量级锁,并且包括像 ConcurrentHashMap 这类并发集合都有在使用 synchronized 关键字配合 cas 来做并发保护,jdk 对于 synchronized 的优化主要在于多重状态锁的升级,最初会使用偏向锁,当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)。
+]]>
而当出现线程尝试进入同步块时发现已有偏向锁,并且是其他线程时,会将锁升级成轻量级锁,并且自旋尝试获取锁,如果自旋成功则表示获取轻量级锁成功,否则将会升级成重量级锁进行阻塞,当然这里具体的还很复杂,说的比较浅薄主体还是想将原先的阻塞互斥锁进行轻量化,区分特殊情况进行加锁。+ +Java ++ +Java +Synchronized +偏向锁 +轻量级锁 +重量级锁 +自旋 ++ +聊聊 Linux 下的 top 命令 +/2021/03/28/%E8%81%8A%E8%81%8A-Linux-%E4%B8%8B%E7%9A%84-top-%E5%91%BD%E4%BB%A4/ +top 命令在日常的 Linux 使用中,特别是做一些服务器的简单状态查看,排查故障都起了比较大的作用,但是由于这个命令看到的东西比较多,一般只会看部分,或者说像我这样就会比较片面地看一些信息,比如默认是进程维度的,可以在启动命令的时候加 +-H进入线程模式 +
+-H :Threads-mode operation + Instructs top to display individual threads. Without this command-line option a summation of all threads in each process is shown. Later + this can be changed with the `H' interactive command.这样就能用在 Java 中去 jstack 中找到对应的线程
+
其实还有比较重要的两个操作,
一个是在 top 启动状态下,按c键,这样能把比如说是一个 Java 进程,具体的进程命令显示出来
像这样
执行前是这样![]()
执行后是这样![]()
第二个就是排序了
+SORTING of task window + + For compatibility, this top supports most of the former top sort keys. Since this is primarily a service to former top users, these commands + do not appear on any help screen. + command sorted-field supported + A start time (non-display) No + M %MEM Yes + N PID Yes + P %CPU Yes + T TIME+ Yes + + Before using any of the following sort provisions, top suggests that you temporarily turn on column highlighting using the `x' interactive com‐ + mand. That will help ensure that the actual sort environment matches your intent. + + The following interactive commands will only be honored when the current sort field is visible. The sort field might not be visible because: + 1) there is insufficient Screen Width + 2) the `f' interactive command turned it Off + + < :Move-Sort-Field-Left + Moves the sort column to the left unless the current sort field is the first field being displayed. + + > :Move-Sort-Field-Right + Moves the sort column to the right unless the current sort field is the last field being displayed.查看 man page 可以找到这一段,其实一般 man page 都是最细致的,只不过因为太多了,有时候懒得看,这里可以通过大写
+]]>M和大写P分别按内存和 CPU 排序,下面还有两个小技巧,通过按 x 可以将当前活跃的排序列用不同颜色标出来,然后可以通过<和>直接左右移动排序列+ +Linux +小技巧 +命令 +top +top +排序 ++ +排序 +linux +小技巧 +top ++ +聊聊 RocketMQ 的 Broker 源码 +/2020/07/19/%E8%81%8A%E8%81%8A-RocketMQ-%E7%9A%84-Broker-%E6%BA%90%E7%A0%81/ +broker 的启动形式有点类似于 NameServer,都是服务类型的,跟 Consumer 差别比较大, + +首先是org.apache.rocketmq.broker.BrokerStartup中的 main 函数,org.apache.rocketmq.broker.BrokerStartup#createBrokerController基本就是读取参数,这里差点把最核心的初始化给漏了,
+
+ +final BrokerController controller = new BrokerController( + brokerConfig, + nettyServerConfig, + nettyClientConfig, + messageStoreConfig); + // remember all configs to prevent discard + controller.getConfiguration().registerConfig(properties); + + boolean initResult = controller.initialize();前面是以 broker 配置,netty 的服务端和客户端配置,以及消息存储配置在实例化 BrokerController,然后就是初始化了
+
+ +public boolean initialize() throws CloneNotSupportedException { + boolean result = this.topicConfigManager.load(); + + result = result && this.consumerOffsetManager.load(); + result = result && this.subscriptionGroupManager.load(); + result = result && this.consumerFilterManager.load();前面这些就是各个配置的 load 了,然后是个我认为比较重要的部分messageStore 的实例化,
+
- Locked ownable synchronizers: - - None +if (result) { + try { + this.messageStore = + new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener, + this.brokerConfig); + if (messageStoreConfig.isEnableDLegerCommitLog()) { + DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore); + ((DLedgerCommitLog)((DefaultMessageStore) messageStore).getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler); + } + this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore); + //load plugin + MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig); + this.messageStore = MessageStoreFactory.build(context, this.messageStore); + this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager)); + } catch (IOException e) { + result = false; + log.error("Failed to initialize", e); + } +} -"DestroyJavaVM" #147 prio=5 os_prio=31 tid=0x00007fc9dd807000 nid=0x2603 waiting on condition [0x0000000000000000] - java.lang.Thread.State: RUNNABLE +result = result && this.messageStore.load();先是实例化,实例化构造函数里的代码比较重要,重点看一下
+
-"http-nio-8080-exec-10" #135 daemon prio=5 os_prio=31 tid=0x00007fc9de1af000 nid=0x9d03 waiting on condition [0x0000700006bad000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) +public DefaultMessageStore(final MessageStoreConfig messageStoreConfig, final BrokerStatsManager brokerStatsManager, + final MessageArrivingListener messageArrivingListener, final BrokerConfig brokerConfig) throws IOException { + this.messageArrivingListener = messageArrivingListener; + this.brokerConfig = brokerConfig; + this.messageStoreConfig = messageStoreConfig; + this.brokerStatsManager = brokerStatsManager; + this.allocateMappedFileService = new AllocateMappedFileService(this); + if (messageStoreConfig.isEnableDLegerCommitLog()) { + this.commitLog = new DLedgerCommitLog(this); + } else { + this.commitLog = new CommitLog(this); + } + this.consumeQueueTable = new ConcurrentHashMap<>(32); -"mythread2" #140 prio=5 os_prio=31 tid=0x00007fc9dd877000 nid=0x9903 waiting on condition [0x0000700006fb9000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f5d4330> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) - at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) - at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) - at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) - at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) - at com.nicksxs.thread_dump_demo.ThreadDumpDemoApplication$2.run(ThreadDumpDemoApplication.java:34) + this.flushConsumeQueueService = new FlushConsumeQueueService(); + this.cleanCommitLogService = new CleanCommitLogService(); + this.cleanConsumeQueueService = new CleanConsumeQueueService(); + this.storeStatsService = new StoreStatsService(); + this.indexService = new IndexService(this); + if (!messageStoreConfig.isEnableDLegerCommitLog()) { + this.haService = new HAService(this); + } else { + this.haService = null; + } + this.reputMessageService = new ReputMessageService(); - Locked ownable synchronizers: - - <0x000000076f5d4360> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) + this.scheduleMessageService = new ScheduleMessageService(this); -"mythread1" #139 prio=5 os_prio=31 tid=0x00007fc9de873800 nid=0x9a03 waiting on condition [0x0000700006eb6000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f5d4360> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) - at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) - at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) - at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) - at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) - at com.nicksxs.thread_dump_demo.ThreadDumpDemoApplication$1.run(ThreadDumpDemoApplication.java:22) + this.transientStorePool = new TransientStorePool(messageStoreConfig); - Locked ownable synchronizers: - - <0x000000076f5d4330> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) + if (messageStoreConfig.isTransientStorePoolEnable()) { + this.transientStorePool.init(); + } -"http-nio-8080-Acceptor" #137 daemon prio=5 os_prio=31 tid=0x00007fc9de1ac000 nid=0x9b03 runnable [0x0000700006db3000] - java.lang.Thread.State: RUNNABLE - at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) - at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422) - at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250) - - locked <0x000000076f1e4820> (a java.lang.Object) - at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:469) - at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:71) - at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:95) - at java.lang.Thread.run(Thread.java:748) + this.allocateMappedFileService.start(); - Locked ownable synchronizers: - - None + this.indexService.start(); -"http-nio-8080-ClientPoller" #136 daemon prio=5 os_prio=31 tid=0x00007fc9dd876800 nid=0x6503 runnable [0x0000700006cb0000] - java.lang.Thread.State: RUNNABLE - at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method) - at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198) - at sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:117) - at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) - - locked <0x000000076f2978c8> (a sun.nio.ch.Util$3) - - locked <0x000000076f2978b8> (a java.util.Collections$UnmodifiableSet) - - locked <0x000000076f297798> (a sun.nio.ch.KQueueSelectorImpl) - at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) - at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:709) - at java.lang.Thread.run(Thread.java:748) + this.dispatcherList = new LinkedList<>(); + this.dispatcherList.addLast(new CommitLogDispatcherBuildConsumeQueue()); + this.dispatcherList.addLast(new CommitLogDispatcherBuildIndex()); - Locked ownable synchronizers: - - None + File file = new File(StorePathConfigHelper.getLockFile(messageStoreConfig.getStorePathRootDir())); + MappedFile.ensureDirOK(file.getParent()); + lockFile = new RandomAccessFile(file, "rw"); + }这里面有很多类,不过先把从构造函数里传进来的忽略下,接下来就是
+AllocateMappedFileService这个service,前面看过文章的可能会根据上面的代码猜到,这也是个 ServiceThread,如果是对RocketMQ 有所了解的可能从名字可以看出这个类是关于 RocketMQ 消息怎么落盘的,当需要创建MappedFile时(在MapedFileQueue.getLastMapedFile方法中),向该线程的requestQueue队列中放入AllocateRequest请求对象,该线程会在后台监听该队列,并在后台创建MapedFile对象,即同时创建了物理文件。然后是创建了 IndexService 服务线程,用来给创建索引;还有是FlushConsumeQueueService是将ConsumeQueue 刷入磁盘;CleanCommitLogService用来清理过期的 CommitLog,默认是 72 小时以上;CleanConsumeQueueService是将小于最新的 CommitLog 偏移量的 ConsumeQueue 清理掉;StoreStatsService是储存统计服务;HAService用于CommitLog 的主备同步;ScheduleMessageService用于定时消息;还有就是这个ReputMessageService非常重要,就是由它实现了将 CommitLog 以 topic+queue 纬度构建 ConsumeQueue,后面TransientStorePool是异步刷盘时的存储buffer,也可以从后面的判断中看出来
- Locked ownable synchronizers: - - None +public boolean isTransientStorePoolEnable() { + return transientStorePoolEnable && FlushDiskType.ASYNC_FLUSH == getFlushDiskType() + && BrokerRole.SLAVE != getBrokerRole(); + }再然后就是启动两个服务线程,dispatcherList是为CommitLog文件转发请求,差不多这个初始化就这些内容。
+然后回到外层,下面是主备切换的配置,然后是数据统计,接着是存储插件加载,然后是往转发器链表里再加一个过滤器
+
-"http-nio-8080-exec-9" #134 daemon prio=5 os_prio=31 tid=0x00007fc9de1ab800 nid=0x6403 waiting on condition [0x0000700006aaa000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) +if (messageStoreConfig.isEnableDLegerCommitLog()) { + DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore); + ((DLedgerCommitLog)((DefaultMessageStore) messageStore).getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler); + } + this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore); + //load plugin + MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig); + this.messageStore = MessageStoreFactory.build(context, this.messageStore); + this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager));接下来就是org.apache.rocketmq.store.MessageStore#load的过程了,
+-
+
- 调用ScheduleMessageService.load方法,初始化延迟级别列表。将这些级别(”1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”)的延时存入延迟级别delayLevelTable:ConcurrentHashMap<Integer /* level */, Long/* delay timeMillis */>变量中,例如1s的kv值为1:1000,5s的kv值为2:5000,key值依次类推;每个延迟级别即为一个队列。 +
2)调用CommitLog.load方法,在此方法中调用MapedFileQueue.load方法,将$HOME /store/commitlog目录下的所有文件加载到MapedFileQueue的List
+变量中; 3)调用DefaultMessageStore.loadConsumeQueue方法加载consumequeue文件数据到DefaultMessageStore.consumeQueueTable集合中。
+初始化StoreCheckPoint对象,加载$HOME/store/checkpoint文件,该文件记录三个字段值,分别是物理队列消息时间戳、逻辑队列消息时间戳、索引队列消息时间戳。
+调用IndexService.load方法加载$HOME/store/index目录下的文件。对该目录下的每个文件初始化一个IndexFile对象。然后调用IndexFile对象的load方法将IndexHeader加载到对象的变量中;再根据检查是否存在abort文件,若有存在abort文件,则表示Broker表示上次是异常退出的,则检查checkpoint的indexMsgTimestamp字段值是否小于IndexHeader的endTimestamp值,indexMsgTimestamp值表示最后刷盘的时间,若小于则表示在最后刷盘之后在该文件中还创建了索引,则要删除该Index文件,否则将该IndexFile对象放入indexFileList:ArrayList
+索引文件集合中。 然后调用org.apache.rocketmq.store.DefaultMessageStore#recover恢复,前面有根据
+boolean lastExitOK = !this.isTempFileExist();临时文件是否存在来判断上一次是否正常退出,根据这个状态来选择什么恢复策略接下去是初始化 Netty 服务端,初始化发送消息线程池(sendMessageExecutor)、拉取消息线程池(pullMessageExecutor)、管理Broker线程池(adminBrokerExecutor)、客户端管理线程池(clientManageExecutor),注册事件处理器,包括发送消息事件处理器(SendMessageProcessor)、拉取消息事件处理器、查询消息事件处理器(QueryMessageProcessor,包括客户端的心跳事件、注销事件、获取消费者列表事件、更新更新和查询消费进度consumerOffset)、客户端管理事件处理器(ClientManageProcessor)、结束事务处理器(EndTransactionProcessor)、默认事件处理器(AdminBrokerProcessor),然后是定时任务
+
+BrokerController.this.getBrokerStats().record();记录 Broker 状态
+BrokerController.this.consumerOffsetManager.persist();持久化consumerOffset
+BrokerController.this.consumerFilterManager.persist();持久化consumerFilter
+BrokerController.this.protectBroker();保护 broker,消费慢,不让继续投递
+BrokerController.this.printWaterMark();打印水位
+log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes());检查落后程度
+BrokerController.this.brokerOuterAPI.fetchNameServerAddr();定时获取 nameserver
+BrokerController.this.printMasterAndSlaveDiff();打印主从不一致然后是 tsl,初始化事务消息,初始化 RPCHook
+请把害怕打到公屏上🤦♂️,从线程池名字和调用的方法应该可以看出大部分的用途
+
-"http-nio-8080-exec-1" #126 daemon prio=5 os_prio=31 tid=0x00007fc9ddb00000 nid=0x5a03 waiting on condition [0x0000700006292000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) - Locked ownable synchronizers: - - None -"http-nio-8080-BlockPoller" #125 daemon prio=5 os_prio=31 tid=0x00007fc9df242000 nid=0xa503 runnable [0x000070000618f000] - java.lang.Thread.State: RUNNABLE - at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method) - at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198) - at sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:117) - at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) - - locked <0x000000076f1eea30> (a sun.nio.ch.Util$3) - - locked <0x000000076f1ee198> (a java.util.Collections$UnmodifiableSet) - - locked <0x000000076f1ee010> (a sun.nio.ch.KQueueSelectorImpl) - at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) - at org.apache.tomcat.util.net.NioBlockingSelector$BlockPoller.run(NioBlockingSelector.java:313) +this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService); + NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone(); + fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2); + this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService); + this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor( + this.brokerConfig.getSendMessageThreadPoolNums(), + this.brokerConfig.getSendMessageThreadPoolNums(), + 1000 * 60, + TimeUnit.MILLISECONDS, + this.sendThreadPoolQueue, + new ThreadFactoryImpl("SendMessageThread_")); - Locked ownable synchronizers: - - None + this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor( + this.brokerConfig.getPullMessageThreadPoolNums(), + this.brokerConfig.getPullMessageThreadPoolNums(), + 1000 * 60, + TimeUnit.MILLISECONDS, + this.pullThreadPoolQueue, + new ThreadFactoryImpl("PullMessageThread_")); -"http-nio-8080-exec-8" #133 daemon prio=5 os_prio=31 tid=0x00007fc9de873000 nid=0x9f03 waiting on condition [0x00007000069a7000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + this.replyMessageExecutor = new BrokerFixedThreadPoolExecutor( + this.brokerConfig.getProcessReplyMessageThreadPoolNums(), + this.brokerConfig.getProcessReplyMessageThreadPoolNums(), + 1000 * 60, + TimeUnit.MILLISECONDS, + this.replyThreadPoolQueue, + new ThreadFactoryImpl("ProcessReplyMessageThread_")); - Locked ownable synchronizers: - - None + this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor( + this.brokerConfig.getQueryMessageThreadPoolNums(), + this.brokerConfig.getQueryMessageThreadPoolNums(), + 1000 * 60, + TimeUnit.MILLISECONDS, + this.queryThreadPoolQueue, + new ThreadFactoryImpl("QueryMessageThread_")); + + this.adminBrokerExecutor = + Executors.newFixedThreadPool(this.brokerConfig.getAdminBrokerThreadPoolNums(), new ThreadFactoryImpl( + "AdminBrokerThread_")); + + this.clientManageExecutor = new ThreadPoolExecutor( + this.brokerConfig.getClientManageThreadPoolNums(), + this.brokerConfig.getClientManageThreadPoolNums(), + 1000 * 60, + TimeUnit.MILLISECONDS, + this.clientManagerThreadPoolQueue, + new ThreadFactoryImpl("ClientManageThread_")); + + this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor( + this.brokerConfig.getHeartbeatThreadPoolNums(), + this.brokerConfig.getHeartbeatThreadPoolNums(), + 1000 * 60, + TimeUnit.MILLISECONDS, + this.heartbeatThreadPoolQueue, + new ThreadFactoryImpl("HeartbeatThread_", true)); + + this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor( + this.brokerConfig.getEndTransactionThreadPoolNums(), + this.brokerConfig.getEndTransactionThreadPoolNums(), + 1000 * 60, + TimeUnit.MILLISECONDS, + this.endTransactionThreadPoolQueue, + new ThreadFactoryImpl("EndTransactionThread_")); + + this.consumerManageExecutor = + Executors.newFixedThreadPool(this.brokerConfig.getConsumerManageThreadPoolNums(), new ThreadFactoryImpl( + "ConsumerManageThread_")); + + this.registerProcessor(); + + final long initialDelay = UtilAll.computeNextMorningTimeMillis() - System.currentTimeMillis(); + final long period = 1000 * 60 * 60 * 24; + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + try { + BrokerController.this.getBrokerStats().record(); + } catch (Throwable e) { + log.error("schedule record error.", e); + } + } + }, initialDelay, period, TimeUnit.MILLISECONDS); -"http-nio-8080-exec-7" #132 daemon prio=5 os_prio=31 tid=0x00007fc9df0a1800 nid=0xa103 waiting on condition [0x00007000068a4000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + try { + BrokerController.this.consumerOffsetManager.persist(); + } catch (Throwable e) { + log.error("schedule persist consumerOffset error.", e); + } + } + }, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS); - Locked ownable synchronizers: - - None + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + try { + BrokerController.this.consumerFilterManager.persist(); + } catch (Throwable e) { + log.error("schedule persist consumer filter error.", e); + } + } + }, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS); -"http-nio-8080-exec-6" #131 daemon prio=5 os_prio=31 tid=0x00007fc9df242800 nid=0x6103 waiting on condition [0x00007000067a1000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + try { + BrokerController.this.protectBroker(); + } catch (Throwable e) { + log.error("protectBroker error.", e); + } + } + }, 3, 3, TimeUnit.MINUTES); - Locked ownable synchronizers: - - None + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + try { + BrokerController.this.printWaterMark(); + } catch (Throwable e) { + log.error("printWaterMark error.", e); + } + } + }, 10, 1, TimeUnit.SECONDS); -"http-nio-8080-exec-5" #130 daemon prio=5 os_prio=31 tid=0x00007fc9de872000 nid=0x5f03 waiting on condition [0x000070000669e000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { - Locked ownable synchronizers: - - None + @Override + public void run() { + try { + log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes()); + } catch (Throwable e) { + log.error("schedule dispatchBehindBytes error.", e); + } + } + }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS); -"http-nio-8080-exec-4" #129 daemon prio=5 os_prio=31 tid=0x00007fc9de1a6000 nid=0x5e03 waiting on condition [0x000070000659b000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + if (this.brokerConfig.getNamesrvAddr() != null) { + this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr()); + log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr()); + } else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) { + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { - Locked ownable synchronizers: - - None + @Override + public void run() { + try { + BrokerController.this.brokerOuterAPI.fetchNameServerAddr(); + } catch (Throwable e) { + log.error("ScheduledTask fetchNameServerAddr exception", e); + } + } + }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS); + } -"http-nio-8080-exec-3" #128 daemon prio=5 os_prio=31 tid=0x00007fc9de871800 nid=0x5c03 waiting on condition [0x0000700006498000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + if (!messageStoreConfig.isEnableDLegerCommitLog()) { + if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) { + if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) { + this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress()); + this.updateMasterHAServerAddrPeriodically = false; + } else { + this.updateMasterHAServerAddrPeriodically = true; + } + } else { + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + try { + BrokerController.this.printMasterAndSlaveDiff(); + } catch (Throwable e) { + log.error("schedule printMasterAndSlaveDiff error.", e); + } + } + }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS); + } + } - Locked ownable synchronizers: - - None + if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) { + // Register a listener to reload SslContext + try { + fileWatchService = new FileWatchService( + new String[] { + TlsSystemConfig.tlsServerCertPath, + TlsSystemConfig.tlsServerKeyPath, + TlsSystemConfig.tlsServerTrustCertPath + }, + new FileWatchService.Listener() { + boolean certChanged, keyChanged = false; -"http-nio-8080-exec-2" #127 daemon prio=5 os_prio=31 tid=0x00007fc9dead9000 nid=0x5b03 waiting on condition [0x0000700006395000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + @Override + public void onChanged(String path) { + if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) { + log.info("The trust certificate changed, reload the ssl context"); + reloadServerSslContext(); + } + if (path.equals(TlsSystemConfig.tlsServerCertPath)) { + certChanged = true; + } + if (path.equals(TlsSystemConfig.tlsServerKeyPath)) { + keyChanged = true; + } + if (certChanged && keyChanged) { + log.info("The certificate and private key changed, reload the ssl context"); + certChanged = keyChanged = false; + reloadServerSslContext(); + } + } - Locked ownable synchronizers: - - None + private void reloadServerSslContext() { + ((NettyRemotingServer) remotingServer).loadSslContext(); + ((NettyRemotingServer) fastRemotingServer).loadSslContext(); + } + }); + } catch (Exception e) { + log.warn("FileWatchService created error, can't load the certificate dynamically"); + } + } + initialTransaction(); + initialAcl(); + initialRpcHooks(); + } + return result;Broker 启动过程
+贴代码
+
- Locked ownable synchronizers: - - None +public void start() throws Exception { + if (this.messageStore != null) { + this.messageStore.start(); + } - Locked ownable synchronizers: - - None + if (this.remotingServer != null) { + this.remotingServer.start(); + } -"container-0" #124 prio=5 os_prio=31 tid=0x00007fc9df06a000 nid=0x5803 waiting on condition [0x000070000608c000] - java.lang.Thread.State: TIMED_WAITING (sleeping) - at java.lang.Thread.sleep(Native Method) - at org.apache.catalina.core.StandardServer.await(StandardServer.java:570) - at org.springframework.boot.web.embedded.tomcat.TomcatWebServer$1.run(TomcatWebServer.java:197) + if (this.fastRemotingServer != null) { + this.fastRemotingServer.start(); + } - Locked ownable synchronizers: - - None + if (this.fileWatchService != null) { + this.fileWatchService.start(); + } -"Catalina-utility-2" #123 prio=1 os_prio=31 tid=0x00007fc9de886000 nid=0xa80f waiting on condition [0x0000700005f89000] - java.lang.Thread.State: WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076c88ab58> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) - at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088) - at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + if (this.brokerOuterAPI != null) { + this.brokerOuterAPI.start(); + } - Locked ownable synchronizers: - - None + if (this.pullRequestHoldService != null) { + this.pullRequestHoldService.start(); + } -"Catalina-utility-1" #122 prio=1 os_prio=31 tid=0x00007fc9de884000 nid=0x5667 waiting on condition [0x0000700005e86000] - java.lang.Thread.State: TIMED_WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x000000076c88ab58> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078) - at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093) - at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) - at java.lang.Thread.run(Thread.java:748) + if (this.clientHousekeepingService != null) { + this.clientHousekeepingService.start(); + } - Locked ownable synchronizers: - - None + if (this.filterServerManager != null) { + this.filterServerManager.start(); + } -"RMI Scheduler(0)" #15 daemon prio=5 os_prio=31 tid=0x00007fc9de9ee000 nid=0x5503 waiting on condition [0x0000700005d83000] - java.lang.Thread.State: TIMED_WAITING (parking) - at sun.misc.Unsafe.park(Native Method) - - parking to wait for <0x00000006c0015410> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078) - at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093) - at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at java.lang.Thread.run(Thread.java:748) + if (!messageStoreConfig.isEnableDLegerCommitLog()) { + startProcessorByHa(messageStoreConfig.getBrokerRole()); + handleSlaveSynchronize(messageStoreConfig.getBrokerRole()); + this.registerBrokerAll(true, false, true); + } - Locked ownable synchronizers: - - None + this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { -"Attach Listener" #13 daemon prio=9 os_prio=31 tid=0x00007fc9df149800 nid=0x3c07 waiting on condition [0x0000000000000000] - java.lang.Thread.State: RUNNABLE + @Override + public void run() { + try { + BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister()); + } catch (Throwable e) { + log.error("registerBrokerAll Exception", e); + } + } + }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS); - Locked ownable synchronizers: - - None + if (this.brokerStatsManager != null) { + this.brokerStatsManager.start(); + } -"RMI TCP Accept-0" #11 daemon prio=5 os_prio=31 tid=0x00007fc9df100000 nid=0x4003 runnable [0x0000700005977000] - java.lang.Thread.State: RUNNABLE - at java.net.PlainSocketImpl.socketAccept(Native Method) - at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409) - at java.net.ServerSocket.implAccept(ServerSocket.java:545) - at java.net.ServerSocket.accept(ServerSocket.java:513) - at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:52) - at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:405) - at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:377) - at java.lang.Thread.run(Thread.java:748) + if (this.brokerFastFailure != null) { + this.brokerFastFailure.start(); + } - Locked ownable synchronizers: - - None -"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007fc9df0ce800 nid=0x4103 runnable [0x0000000000000000] - java.lang.Thread.State: RUNNABLE + }首先是启动messageStore,调用 start 方法,这里面又调用了一些代码
+
-"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007fc9df0ca000 nid=0x3303 runnable [0x0000700005468000] - java.lang.Thread.State: RUNNABLE - at java.net.SocketInputStream.socketRead0(Native Method) - at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) - at java.net.SocketInputStream.read(SocketInputStream.java:171) - at java.net.SocketInputStream.read(SocketInputStream.java:141) - at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) - at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) - at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - - locked <0x00000006c001b760> (a java.io.InputStreamReader) - at java.io.InputStreamReader.read(InputStreamReader.java:184) - at java.io.BufferedReader.fill(BufferedReader.java:161) - at java.io.BufferedReader.readLine(BufferedReader.java:324) - - locked <0x00000006c001b760> (a java.io.InputStreamReader) - at java.io.BufferedReader.readLine(BufferedReader.java:389) - at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64) - Locked ownable synchronizers: - - None -"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fc9de824000 nid=0x4503 runnable [0x0000000000000000] +public void start() throws Exception { -"C1 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007fc9df0ce000 nid=0x4203 waiting on condition [0x0000000000000000] - java.lang.Thread.State: RUNNABLE + lock = lockFile.getChannel().tryLock(0, 1, false); + if (lock == null || lock.isShared() || !lock.isValid()) { + throw new RuntimeException("Lock failed,MQ already started"); + } - Locked ownable synchronizers: - - None + lockFile.getChannel().write(ByteBuffer.wrap("lock".getBytes())); + lockFile.getChannel().force(true); + { + /** + * 1. Make sure the fast-forward messages to be truncated during the recovering according to the max physical offset of the commitlog; + * 2. DLedger committedPos may be missing, so the maxPhysicalPosInLogicQueue maybe bigger that maxOffset returned by DLedgerCommitLog, just let it go; + * 3. Calculate the reput offset according to the consume queue; + * 4. Make sure the fall-behind messages to be dispatched before starting the commitlog, especially when the broker role are automatically changed. + */ + long maxPhysicalPosInLogicQueue = commitLog.getMinOffset(); + for (ConcurrentMap<Integer, ConsumeQueue> maps : this.consumeQueueTable.values()) { + for (ConsumeQueue logic : maps.values()) { + if (logic.getMaxPhysicOffset() > maxPhysicalPosInLogicQueue) { + maxPhysicalPosInLogicQueue = logic.getMaxPhysicOffset(); + } + } + } + if (maxPhysicalPosInLogicQueue < 0) { + maxPhysicalPosInLogicQueue = 0; + } + if (maxPhysicalPosInLogicQueue < this.commitLog.getMinOffset()) { + maxPhysicalPosInLogicQueue = this.commitLog.getMinOffset(); + /** + * This happens in following conditions: + * 1. If someone removes all the consumequeue files or the disk get damaged. + * 2. Launch a new broker, and copy the commitlog from other brokers. + * + * All the conditions has the same in common that the maxPhysicalPosInLogicQueue should be 0. + * If the maxPhysicalPosInLogicQueue is gt 0, there maybe something wrong. + */ + log.warn("[TooSmallCqOffset] maxPhysicalPosInLogicQueue={} clMinOffset={}", maxPhysicalPosInLogicQueue, this.commitLog.getMinOffset()); + } + log.info("[SetReputOffset] maxPhysicalPosInLogicQueue={} clMinOffset={} clMaxOffset={} clConfirmedOffset={}", + maxPhysicalPosInLogicQueue, this.commitLog.getMinOffset(), this.commitLog.getMaxOffset(), this.commitLog.getConfirmOffset()); + this.reputMessageService.setReputFromOffset(maxPhysicalPosInLogicQueue); + this.reputMessageService.start(); -"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007fc9de0a3800 nid=0x3503 waiting on condition [0x0000000000000000] - java.lang.Thread.State: RUNNABLE + /** + * 1. Finish dispatching the messages fall behind, then to start other services. + * 2. DLedger committedPos may be missing, so here just require dispatchBehindBytes <= 0 + */ + while (true) { + if (dispatchBehindBytes() <= 0) { + break; + } + Thread.sleep(1000); + log.info("Try to finish doing reput the messages fall behind during the starting, reputOffset={} maxOffset={} behind={}", this.reputMessageService.getReputFromOffset(), this.getMaxPhyOffset(), this.dispatchBehindBytes()); + } + this.recoverTopicQueueTable(); + } - Locked ownable synchronizers: - - None + if (!messageStoreConfig.isEnableDLegerCommitLog()) { + this.haService.start(); + this.handleScheduleMessageService(messageStoreConfig.getBrokerRole()); + } -"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007fc9de89b000 nid=0x3403 waiting on condition [0x0000000000000000] - java.lang.Thread.State: RUNNABLE + this.flushConsumeQueueService.start(); + this.commitLog.start(); + this.storeStatsService.start(); - Locked ownable synchronizers: - - None + this.createTempFile(); + this.addScheduleTask(); + this.shutdown = false; + }调用DefaultMessageStore.start方法启动DefaultMessageStore对象中的一些服务线程。
+-
+
- 启动ReputMessageService服务线程 +
- 启动FlushConsumeQueueService服务线程; +
- 调用CommitLog.start方法,启动CommitLog对象中的FlushCommitLogService线程服务,若是同步刷盘(SYNC_FLUSH)则是启动GroupCommitService线程服务;若是异步刷盘(ASYNC_FLUSH)则是启动FlushRealTimeService线程服务; +
- 启动StoreStatsService线程服务; +
- 启动定时清理任务 +
然后是启动ClientHousekeepingService的 netty 服务端和客户端,然后是启动fileWatchService证书服务,接着启动BrokerOuterAPI中的NettyRemotingClient,即建立与NameServer的链接,用于自身Broker与其他模块的RPC功能调用;包括获取NameServer的地址、注册Broker、注销Broker、获取Topic配置、获取消息进度信息、获取订阅关系等RPC功能,然后是PullRequestHoldService服务线程,这个就是实现长轮询的,然后启动管家ClientHousekeepingService服务,负责扫描不活跃的生产者,消费者和 filter,启动FilterServerManager 过滤器服务管理,然后启动定时任务调用org.apache.rocketmq.broker.BrokerController#registerBrokerAll向所有 nameserver 注册 broker,最后是按需开启org.apache.rocketmq.store.stats.BrokerStatsManager和org.apache.rocketmq.broker.latency.BrokerFastFailure,基本上启动过程就完成了
+]]>+ +MQ +RocketMQ +消息队列 +RocketMQ +中间件 +RocketMQ ++ +MQ +消息队列 +RocketMQ +削峰填谷 +中间件 +源码解析 +Broker ++ -聊聊 Java 自带的那些*逆天*工具 +/2020/08/02/%E8%81%8A%E8%81%8A-Java-%E8%87%AA%E5%B8%A6%E7%9A%84%E9%82%A3%E4%BA%9B%E9%80%86%E5%A4%A9%E5%B7%A5%E5%85%B7/ +原谅我的标题党,其实这些工具的确很厉害,之前其实介绍过一点相关的,是从我一次问题排查的过程中用到的,但是最近又有碰到一次排查问题,发现其实用 idea 直接 -dump thread是不现实的,毕竟服务器环境的没法这么操作,那就得用 Java 的那些工具了 +jstack & jps
譬如
+jstack,这个命令其实不能更简单了
看看 help 信息![]()
用-l参数可以打出锁的额外信息,然后后面的 pid 就是进程 id 咯,机智的小伙伴会问了(就你这个小白才问这么蠢的问题🤦♂️),怎么看 Java 应用的进程呢
那就是jps了,命令也很简单,一般直接用jps命令就好了,不过也可以 help 看一下![]()
稍微解释下,-q是只显示进程 id,-m是输出给main 方法的参数,比如我在配置中加给参数![]()
然后用jps -m查看![]()
-v加上小 v 的话就是打印 jvm 参数![]()
还是有点东西,然后就继续介绍 jstack 了,然后我们看看 jstack 出来是啥,为了加点内容我加了个死锁
+public static void main(String[] args) throws InterruptedException { + SpringApplication.run(ThreadDumpDemoApplication.class, args); + ReentrantLock lock1 = new ReentrantLock(); + ReentrantLock lock2 = new ReentrantLock(); + Thread t1 = new Thread() { + @Override + public void run() { + try { + lock1.lock(); + TimeUnit.SECONDS.sleep(1); + lock2.lock(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + Thread t2 = new Thread() { + @Override + public void run() { + try { + lock2.lock(); + TimeUnit.SECONDS.sleep(1); + lock1.lock(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + t1.setName("mythread1"); + t2.setName("mythread2"); + t1.start(); + t2.start(); + Thread.sleep(10000); + }然后看看出来时怎么样的
+
-2020-08-02 21:50:32 +Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.201-b09 mixed mode): + +"DestroyJavaVM" #147 prio=5 os_prio=31 tid=0x00007fc9dd807000 nid=0x2603 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None -"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fc9dd811800 nid=0x4f03 in Object.wait() [0x0000700005262000] - java.lang.Thread.State: WAITING (on object monitor) - at java.lang.Object.wait(Native Method) - - waiting on <0x00000006c0008348> (a java.lang.ref.ReferenceQueue$Lock) - at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) - - locked <0x00000006c0008348> (a java.lang.ref.ReferenceQueue$Lock) - at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) - at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) - - Locked ownable synchronizers: - - None - -"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fc9de02a000 nid=0x5003 in Object.wait() [0x000070000515f000] - java.lang.Thread.State: WAITING (on object monitor) - at java.lang.Object.wait(Native Method) - - waiting on <0x00000006c001b940> (a java.lang.ref.Reference$Lock) - at java.lang.Object.wait(Object.java:502) - at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - - locked <0x00000006c001b940> (a java.lang.ref.Reference$Lock) - at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) - - Locked ownable synchronizers: - - None - -"VM Thread" os_prio=31 tid=0x00007fc9df00b800 nid=0x2c03 runnable - -"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fc9de805000 nid=0x1e07 runnable - -"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fc9de003800 nid=0x2a03 runnable - -"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fc9df002000 nid=0x5403 runnable - -"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fc9df002800 nid=0x5203 runnable - -"VM Periodic Task Thread" os_prio=31 tid=0x00007fc9df11a800 nid=0x3a03 waiting on condition - -JNI global references: 1087 - - -Found one Java-level deadlock: -============================= -"mythread2": - waiting for ownable synchronizer 0x000000076f5d4330, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), - which is held by "mythread1" -"mythread1": - waiting for ownable synchronizer 0x000000076f5d4360, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), - which is held by "mythread2" - -Java stack information for the threads listed above: -=================================================== -"mythread2": +"mythread2" #140 prio=5 os_prio=31 tid=0x00007fc9dd877000 nid=0x9903 waiting on condition [0x0000700006fb9000] + java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076f5d4330> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) @@ -12081,7 +12221,12 @@ JNI global references: java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.nicksxs.thread_dump_demo.ThreadDumpDemoApplication$2.run(ThreadDumpDemoApplication.java:34) -"mythread1": + + Locked ownable synchronizers: + - <0x000000076f5d4360> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) + +"mythread1" #139 prio=5 os_prio=31 tid=0x00007fc9de873800 nid=0x9a03 waiting on condition [0x0000700006eb6000] + java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076f5d4360> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) @@ -12092,595 +12237,450 @@ JNI global references: java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.nicksxs.thread_dump_demo.ThreadDumpDemoApplication$1.run(ThreadDumpDemoApplication.java:22) -Found 1 deadlock.前面的信息其实上次就看过了,后面就可以发现有个死锁了,
-![]()
上面比较长,把主要的截出来,就是这边的,这点就很强大了。jmap
惯例还是看一下帮助信息
-![]()
这个相对命令比较多,不过因为现在 dump 下来我们可能会用文件模式,然后将文件下载下来使用 mat 进行分析,所以可以使用jmap -dump:live,format=b,file=heap.bin <pid>
命令照着上面看的就是打印活着的对象,然后以二进制格式,文件名叫 heap.bin 然后最后就是进程 id,打印出来以后可以用 mat 打开![]()
这样就可以很清晰的看到应用里的各种信息,jmap 直接在命令中还可以看很多信息,比如使用jmap -histo <pid>打印对象的实例数和对象占用的内存![]()
jmap -finalizerinfo <pid>打印正在等候回收的对象![]()
小tips
对于一些应用内存已经占满了,jstack 和 jmap 可能会连不上的情况,可以使用
-]]>-F参数强制打印线程或者 dump 文件,但是要注意这两者使用的用户必须与 java 进程启动用户一致,并且使用的 jdk 也要一致- -Java -Thread dump -问题排查 -工具 -- -Java -JPS -JStack -JMap -- -聊聊 Linux 下的 top 命令 -/2021/03/28/%E8%81%8A%E8%81%8A-Linux-%E4%B8%8B%E7%9A%84-top-%E5%91%BD%E4%BB%A4/ -top 命令在日常的 Linux 使用中,特别是做一些服务器的简单状态查看,排查故障都起了比较大的作用,但是由于这个命令看到的东西比较多,一般只会看部分,或者说像我这样就会比较片面地看一些信息,比如默认是进程维度的,可以在启动命令的时候加 --H进入线程模式 -
--H :Threads-mode operation - Instructs top to display individual threads. Without this command-line option a summation of all threads in each process is shown. Later - this can be changed with the `H' interactive command.这样就能用在 Java 中去 jstack 中找到对应的线程
-
其实还有比较重要的两个操作,
一个是在 top 启动状态下,按c键,这样能把比如说是一个 Java 进程,具体的进程命令显示出来
像这样
执行前是这样![]()
执行后是这样![]()
第二个就是排序了
-SORTING of task window - - For compatibility, this top supports most of the former top sort keys. Since this is primarily a service to former top users, these commands - do not appear on any help screen. - command sorted-field supported - A start time (non-display) No - M %MEM Yes - N PID Yes - P %CPU Yes - T TIME+ Yes - - Before using any of the following sort provisions, top suggests that you temporarily turn on column highlighting using the `x' interactive com‐ - mand. That will help ensure that the actual sort environment matches your intent. - - The following interactive commands will only be honored when the current sort field is visible. The sort field might not be visible because: - 1) there is insufficient Screen Width - 2) the `f' interactive command turned it Off - - < :Move-Sort-Field-Left - Moves the sort column to the left unless the current sort field is the first field being displayed. - - > :Move-Sort-Field-Right - Moves the sort column to the right unless the current sort field is the last field being displayed.查看 man page 可以找到这一段,其实一般 man page 都是最细致的,只不过因为太多了,有时候懒得看,这里可以通过大写
-]]>M和大写P分别按内存和 CPU 排序,下面还有两个小技巧,通过按 x 可以将当前活跃的排序列用不同颜色标出来,然后可以通过<和>直接左右移动排序列- -Linux -小技巧 -命令 -top -top -排序 -- -排序 -linux -小技巧 -top -- 聊聊 RocketMQ 的 Broker 源码 -/2020/07/19/%E8%81%8A%E8%81%8A-RocketMQ-%E7%9A%84-Broker-%E6%BA%90%E7%A0%81/ -broker 的启动形式有点类似于 NameServer,都是服务类型的,跟 Consumer 差别比较大, - 首先是org.apache.rocketmq.broker.BrokerStartup中的 main 函数,org.apache.rocketmq.broker.BrokerStartup#createBrokerController基本就是读取参数,这里差点把最核心的初始化给漏了,
-
- -final BrokerController controller = new BrokerController( - brokerConfig, - nettyServerConfig, - nettyClientConfig, - messageStoreConfig); - // remember all configs to prevent discard - controller.getConfiguration().registerConfig(properties); - - boolean initResult = controller.initialize();前面是以 broker 配置,netty 的服务端和客户端配置,以及消息存储配置在实例化 BrokerController,然后就是初始化了
-
+ Locked ownable synchronizers: + - <0x000000076f5d4330> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) -public boolean initialize() throws CloneNotSupportedException { - boolean result = this.topicConfigManager.load(); - - result = result && this.consumerOffsetManager.load(); - result = result && this.subscriptionGroupManager.load(); - result = result && this.consumerFilterManager.load();前面这些就是各个配置的 load 了,然后是个我认为比较重要的部分messageStore 的实例化,
-
+ Locked ownable synchronizers: + - None -if (result) { - try { - this.messageStore = - new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener, - this.brokerConfig); - if (messageStoreConfig.isEnableDLegerCommitLog()) { - DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore); - ((DLedgerCommitLog)((DefaultMessageStore) messageStore).getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler); - } - this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore); - //load plugin - MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig); - this.messageStore = MessageStoreFactory.build(context, this.messageStore); - this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager)); - } catch (IOException e) { - result = false; - log.error("Failed to initialize", e); - } -} +"http-nio-8080-Acceptor" #137 daemon prio=5 os_prio=31 tid=0x00007fc9de1ac000 nid=0x9b03 runnable [0x0000700006db3000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) + at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422) + at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250) + - locked <0x000000076f1e4820> (a java.lang.Object) + at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:469) + at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:71) + at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:95) + at java.lang.Thread.run(Thread.java:748) -result = result && this.messageStore.load();先是实例化,实例化构造函数里的代码比较重要,重点看一下
-
+"http-nio-8080-exec-7" #132 daemon prio=5 os_prio=31 tid=0x00007fc9df0a1800 nid=0xa103 waiting on condition [0x00007000068a4000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) -public DefaultMessageStore(final MessageStoreConfig messageStoreConfig, final BrokerStatsManager brokerStatsManager, - final MessageArrivingListener messageArrivingListener, final BrokerConfig brokerConfig) throws IOException { - this.messageArrivingListener = messageArrivingListener; - this.brokerConfig = brokerConfig; - this.messageStoreConfig = messageStoreConfig; - this.brokerStatsManager = brokerStatsManager; - this.allocateMappedFileService = new AllocateMappedFileService(this); - if (messageStoreConfig.isEnableDLegerCommitLog()) { - this.commitLog = new DLedgerCommitLog(this); - } else { - this.commitLog = new CommitLog(this); - } - this.consumeQueueTable = new ConcurrentHashMap<>(32); +"http-nio-8080-ClientPoller" #136 daemon prio=5 os_prio=31 tid=0x00007fc9dd876800 nid=0x6503 runnable [0x0000700006cb0000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method) + at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198) + at sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:117) + at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) + - locked <0x000000076f2978c8> (a sun.nio.ch.Util$3) + - locked <0x000000076f2978b8> (a java.util.Collections$UnmodifiableSet) + - locked <0x000000076f297798> (a sun.nio.ch.KQueueSelectorImpl) + at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) + at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:709) + at java.lang.Thread.run(Thread.java:748) - this.flushConsumeQueueService = new FlushConsumeQueueService(); - this.cleanCommitLogService = new CleanCommitLogService(); - this.cleanConsumeQueueService = new CleanConsumeQueueService(); - this.storeStatsService = new StoreStatsService(); - this.indexService = new IndexService(this); - if (!messageStoreConfig.isEnableDLegerCommitLog()) { - this.haService = new HAService(this); - } else { - this.haService = null; - } - this.reputMessageService = new ReputMessageService(); + Locked ownable synchronizers: + - None - this.scheduleMessageService = new ScheduleMessageService(this); +"http-nio-8080-exec-10" #135 daemon prio=5 os_prio=31 tid=0x00007fc9de1af000 nid=0x9d03 waiting on condition [0x0000700006bad000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.transientStorePool = new TransientStorePool(messageStoreConfig); + Locked ownable synchronizers: + - None - if (messageStoreConfig.isTransientStorePoolEnable()) { - this.transientStorePool.init(); - } +"http-nio-8080-exec-9" #134 daemon prio=5 os_prio=31 tid=0x00007fc9de1ab800 nid=0x6403 waiting on condition [0x0000700006aaa000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.allocateMappedFileService.start(); + Locked ownable synchronizers: + - None - this.indexService.start(); +"http-nio-8080-exec-8" #133 daemon prio=5 os_prio=31 tid=0x00007fc9de873000 nid=0x9f03 waiting on condition [0x00007000069a7000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.dispatcherList = new LinkedList<>(); - this.dispatcherList.addLast(new CommitLogDispatcherBuildConsumeQueue()); - this.dispatcherList.addLast(new CommitLogDispatcherBuildIndex()); + Locked ownable synchronizers: + - None - File file = new File(StorePathConfigHelper.getLockFile(messageStoreConfig.getStorePathRootDir())); - MappedFile.ensureDirOK(file.getParent()); - lockFile = new RandomAccessFile(file, "rw"); - }这里面有很多类,不过先把从构造函数里传进来的忽略下,接下来就是
-AllocateMappedFileService这个service,前面看过文章的可能会根据上面的代码猜到,这也是个 ServiceThread,如果是对RocketMQ 有所了解的可能从名字可以看出这个类是关于 RocketMQ 消息怎么落盘的,当需要创建MappedFile时(在MapedFileQueue.getLastMapedFile方法中),向该线程的requestQueue队列中放入AllocateRequest请求对象,该线程会在后台监听该队列,并在后台创建MapedFile对象,即同时创建了物理文件。然后是创建了 IndexService 服务线程,用来给创建索引;还有是FlushConsumeQueueService是将ConsumeQueue 刷入磁盘;CleanCommitLogService用来清理过期的 CommitLog,默认是 72 小时以上;CleanConsumeQueueService是将小于最新的 CommitLog 偏移量的 ConsumeQueue 清理掉;StoreStatsService是储存统计服务;HAService用于CommitLog 的主备同步;ScheduleMessageService用于定时消息;还有就是这个ReputMessageService非常重要,就是由它实现了将 CommitLog 以 topic+queue 纬度构建 ConsumeQueue,后面TransientStorePool是异步刷盘时的存储buffer,也可以从后面的判断中看出来
+ Locked ownable synchronizers: + - None -public boolean isTransientStorePoolEnable() { - return transientStorePoolEnable && FlushDiskType.ASYNC_FLUSH == getFlushDiskType() - && BrokerRole.SLAVE != getBrokerRole(); - }再然后就是启动两个服务线程,dispatcherList是为CommitLog文件转发请求,差不多这个初始化就这些内容。
-然后回到外层,下面是主备切换的配置,然后是数据统计,接着是存储插件加载,然后是往转发器链表里再加一个过滤器
-
+"http-nio-8080-exec-6" #131 daemon prio=5 os_prio=31 tid=0x00007fc9df242800 nid=0x6103 waiting on condition [0x00007000067a1000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) -if (messageStoreConfig.isEnableDLegerCommitLog()) { - DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore); - ((DLedgerCommitLog)((DefaultMessageStore) messageStore).getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler); - } - this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore); - //load plugin - MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig); - this.messageStore = MessageStoreFactory.build(context, this.messageStore); - this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager));接下来就是org.apache.rocketmq.store.MessageStore#load的过程了,
--
-
- 调用ScheduleMessageService.load方法,初始化延迟级别列表。将这些级别(”1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”)的延时存入延迟级别delayLevelTable:ConcurrentHashMap<Integer /* level */, Long/* delay timeMillis */>变量中,例如1s的kv值为1:1000,5s的kv值为2:5000,key值依次类推;每个延迟级别即为一个队列。 -
2)调用CommitLog.load方法,在此方法中调用MapedFileQueue.load方法,将$HOME /store/commitlog目录下的所有文件加载到MapedFileQueue的List
-变量中; 3)调用DefaultMessageStore.loadConsumeQueue方法加载consumequeue文件数据到DefaultMessageStore.consumeQueueTable集合中。
-初始化StoreCheckPoint对象,加载$HOME/store/checkpoint文件,该文件记录三个字段值,分别是物理队列消息时间戳、逻辑队列消息时间戳、索引队列消息时间戳。
-调用IndexService.load方法加载$HOME/store/index目录下的文件。对该目录下的每个文件初始化一个IndexFile对象。然后调用IndexFile对象的load方法将IndexHeader加载到对象的变量中;再根据检查是否存在abort文件,若有存在abort文件,则表示Broker表示上次是异常退出的,则检查checkpoint的indexMsgTimestamp字段值是否小于IndexHeader的endTimestamp值,indexMsgTimestamp值表示最后刷盘的时间,若小于则表示在最后刷盘之后在该文件中还创建了索引,则要删除该Index文件,否则将该IndexFile对象放入indexFileList:ArrayList
-索引文件集合中。 然后调用org.apache.rocketmq.store.DefaultMessageStore#recover恢复,前面有根据
-boolean lastExitOK = !this.isTempFileExist();临时文件是否存在来判断上一次是否正常退出,根据这个状态来选择什么恢复策略接下去是初始化 Netty 服务端,初始化发送消息线程池(sendMessageExecutor)、拉取消息线程池(pullMessageExecutor)、管理Broker线程池(adminBrokerExecutor)、客户端管理线程池(clientManageExecutor),注册事件处理器,包括发送消息事件处理器(SendMessageProcessor)、拉取消息事件处理器、查询消息事件处理器(QueryMessageProcessor,包括客户端的心跳事件、注销事件、获取消费者列表事件、更新更新和查询消费进度consumerOffset)、客户端管理事件处理器(ClientManageProcessor)、结束事务处理器(EndTransactionProcessor)、默认事件处理器(AdminBrokerProcessor),然后是定时任务
-
-BrokerController.this.getBrokerStats().record();记录 Broker 状态
-BrokerController.this.consumerOffsetManager.persist();持久化consumerOffset
-BrokerController.this.consumerFilterManager.persist();持久化consumerFilter
-BrokerController.this.protectBroker();保护 broker,消费慢,不让继续投递
-BrokerController.this.printWaterMark();打印水位
-log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes());检查落后程度
-BrokerController.this.brokerOuterAPI.fetchNameServerAddr();定时获取 nameserver
-BrokerController.this.printMasterAndSlaveDiff();打印主从不一致然后是 tsl,初始化事务消息,初始化 RPCHook
-请把害怕打到公屏上🤦♂️,从线程池名字和调用的方法应该可以看出大部分的用途
-
+"RMI TCP Accept-0" #11 daemon prio=5 os_prio=31 tid=0x00007fc9df100000 nid=0x4003 runnable [0x0000700005977000] + java.lang.Thread.State: RUNNABLE + at java.net.PlainSocketImpl.socketAccept(Native Method) + at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409) + at java.net.ServerSocket.implAccept(ServerSocket.java:545) + at java.net.ServerSocket.accept(ServerSocket.java:513) + at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:52) + at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:405) + at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:377) + at java.lang.Thread.run(Thread.java:748) + Locked ownable synchronizers: + - None +"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007fc9df0ce800 nid=0x4103 runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE -this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService); - NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone(); - fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2); - this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService); - this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor( - this.brokerConfig.getSendMessageThreadPoolNums(), - this.brokerConfig.getSendMessageThreadPoolNums(), - 1000 * 60, - TimeUnit.MILLISECONDS, - this.sendThreadPoolQueue, - new ThreadFactoryImpl("SendMessageThread_")); + Locked ownable synchronizers: + - None - this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor( - this.brokerConfig.getPullMessageThreadPoolNums(), - this.brokerConfig.getPullMessageThreadPoolNums(), - 1000 * 60, - TimeUnit.MILLISECONDS, - this.pullThreadPoolQueue, - new ThreadFactoryImpl("PullMessageThread_")); +"http-nio-8080-exec-5" #130 daemon prio=5 os_prio=31 tid=0x00007fc9de872000 nid=0x5f03 waiting on condition [0x000070000669e000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.replyMessageExecutor = new BrokerFixedThreadPoolExecutor( - this.brokerConfig.getProcessReplyMessageThreadPoolNums(), - this.brokerConfig.getProcessReplyMessageThreadPoolNums(), - 1000 * 60, - TimeUnit.MILLISECONDS, - this.replyThreadPoolQueue, - new ThreadFactoryImpl("ProcessReplyMessageThread_")); + Locked ownable synchronizers: + - None - this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor( - this.brokerConfig.getQueryMessageThreadPoolNums(), - this.brokerConfig.getQueryMessageThreadPoolNums(), - 1000 * 60, - TimeUnit.MILLISECONDS, - this.queryThreadPoolQueue, - new ThreadFactoryImpl("QueryMessageThread_")); +"http-nio-8080-exec-4" #129 daemon prio=5 os_prio=31 tid=0x00007fc9de1a6000 nid=0x5e03 waiting on condition [0x000070000659b000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.adminBrokerExecutor = - Executors.newFixedThreadPool(this.brokerConfig.getAdminBrokerThreadPoolNums(), new ThreadFactoryImpl( - "AdminBrokerThread_")); + Locked ownable synchronizers: + - None - this.clientManageExecutor = new ThreadPoolExecutor( - this.brokerConfig.getClientManageThreadPoolNums(), - this.brokerConfig.getClientManageThreadPoolNums(), - 1000 * 60, - TimeUnit.MILLISECONDS, - this.clientManagerThreadPoolQueue, - new ThreadFactoryImpl("ClientManageThread_")); +"http-nio-8080-exec-3" #128 daemon prio=5 os_prio=31 tid=0x00007fc9de871800 nid=0x5c03 waiting on condition [0x0000700006498000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor( - this.brokerConfig.getHeartbeatThreadPoolNums(), - this.brokerConfig.getHeartbeatThreadPoolNums(), - 1000 * 60, - TimeUnit.MILLISECONDS, - this.heartbeatThreadPoolQueue, - new ThreadFactoryImpl("HeartbeatThread_", true)); + Locked ownable synchronizers: + - None - this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor( - this.brokerConfig.getEndTransactionThreadPoolNums(), - this.brokerConfig.getEndTransactionThreadPoolNums(), - 1000 * 60, - TimeUnit.MILLISECONDS, - this.endTransactionThreadPoolQueue, - new ThreadFactoryImpl("EndTransactionThread_")); +"http-nio-8080-exec-2" #127 daemon prio=5 os_prio=31 tid=0x00007fc9dead9000 nid=0x5b03 waiting on condition [0x0000700006395000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.consumerManageExecutor = - Executors.newFixedThreadPool(this.brokerConfig.getConsumerManageThreadPoolNums(), new ThreadFactoryImpl( - "ConsumerManageThread_")); + Locked ownable synchronizers: + - None + +"http-nio-8080-exec-1" #126 daemon prio=5 os_prio=31 tid=0x00007fc9ddb00000 nid=0x5a03 waiting on condition [0x0000700006292000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f26aa00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107) + at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.registerProcessor(); + Locked ownable synchronizers: + - None - final long initialDelay = UtilAll.computeNextMorningTimeMillis() - System.currentTimeMillis(); - final long period = 1000 * 60 * 60 * 24; - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - BrokerController.this.getBrokerStats().record(); - } catch (Throwable e) { - log.error("schedule record error.", e); - } - } - }, initialDelay, period, TimeUnit.MILLISECONDS); +"http-nio-8080-BlockPoller" #125 daemon prio=5 os_prio=31 tid=0x00007fc9df242000 nid=0xa503 runnable [0x000070000618f000] + java.lang.Thread.State: RUNNABLE + at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method) + at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198) + at sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:117) + at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) + - locked <0x000000076f1eea30> (a sun.nio.ch.Util$3) + - locked <0x000000076f1ee198> (a java.util.Collections$UnmodifiableSet) + - locked <0x000000076f1ee010> (a sun.nio.ch.KQueueSelectorImpl) + at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) + at org.apache.tomcat.util.net.NioBlockingSelector$BlockPoller.run(NioBlockingSelector.java:313) - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - BrokerController.this.consumerOffsetManager.persist(); - } catch (Throwable e) { - log.error("schedule persist consumerOffset error.", e); - } - } - }, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS); + Locked ownable synchronizers: + - None - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - BrokerController.this.consumerFilterManager.persist(); - } catch (Throwable e) { - log.error("schedule persist consumer filter error.", e); - } - } - }, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS); +"container-0" #124 prio=5 os_prio=31 tid=0x00007fc9df06a000 nid=0x5803 waiting on condition [0x000070000608c000] + java.lang.Thread.State: TIMED_WAITING (sleeping) + at java.lang.Thread.sleep(Native Method) + at org.apache.catalina.core.StandardServer.await(StandardServer.java:570) + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer$1.run(TomcatWebServer.java:197) - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - BrokerController.this.protectBroker(); - } catch (Throwable e) { - log.error("protectBroker error.", e); - } - } - }, 3, 3, TimeUnit.MINUTES); + Locked ownable synchronizers: + - None - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - BrokerController.this.printWaterMark(); - } catch (Throwable e) { - log.error("printWaterMark error.", e); - } - } - }, 10, 1, TimeUnit.SECONDS); +"Catalina-utility-2" #123 prio=1 os_prio=31 tid=0x00007fc9de886000 nid=0xa80f waiting on condition [0x0000700005f89000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076c88ab58> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + Locked ownable synchronizers: + - None - @Override - public void run() { - try { - log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes()); - } catch (Throwable e) { - log.error("schedule dispatchBehindBytes error.", e); - } - } - }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS); +"Catalina-utility-1" #122 prio=1 os_prio=31 tid=0x00007fc9de884000 nid=0x5667 waiting on condition [0x0000700005e86000] + java.lang.Thread.State: TIMED_WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076c88ab58> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Thread.java:748) - if (this.brokerConfig.getNamesrvAddr() != null) { - this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr()); - log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr()); - } else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) { - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + Locked ownable synchronizers: + - None - @Override - public void run() { - try { - BrokerController.this.brokerOuterAPI.fetchNameServerAddr(); - } catch (Throwable e) { - log.error("ScheduledTask fetchNameServerAddr exception", e); - } - } - }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS); - } +"RMI Scheduler(0)" #15 daemon prio=5 os_prio=31 tid=0x00007fc9de9ee000 nid=0x5503 waiting on condition [0x0000700005d83000] + java.lang.Thread.State: TIMED_WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x00000006c0015410> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:748) - if (!messageStoreConfig.isEnableDLegerCommitLog()) { - if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) { - if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) { - this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress()); - this.updateMasterHAServerAddrPeriodically = false; - } else { - this.updateMasterHAServerAddrPeriodically = true; - } - } else { - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - BrokerController.this.printMasterAndSlaveDiff(); - } catch (Throwable e) { - log.error("schedule printMasterAndSlaveDiff error.", e); - } - } - }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS); - } - } + Locked ownable synchronizers: + - None - if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) { - // Register a listener to reload SslContext - try { - fileWatchService = new FileWatchService( - new String[] { - TlsSystemConfig.tlsServerCertPath, - TlsSystemConfig.tlsServerKeyPath, - TlsSystemConfig.tlsServerTrustCertPath - }, - new FileWatchService.Listener() { - boolean certChanged, keyChanged = false; +"Attach Listener" #13 daemon prio=9 os_prio=31 tid=0x00007fc9df149800 nid=0x3c07 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE - @Override - public void onChanged(String path) { - if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) { - log.info("The trust certificate changed, reload the ssl context"); - reloadServerSslContext(); - } - if (path.equals(TlsSystemConfig.tlsServerCertPath)) { - certChanged = true; - } - if (path.equals(TlsSystemConfig.tlsServerKeyPath)) { - keyChanged = true; - } - if (certChanged && keyChanged) { - log.info("The certificate and private key changed, reload the ssl context"); - certChanged = keyChanged = false; - reloadServerSslContext(); - } - } + Locked ownable synchronizers: + - None - private void reloadServerSslContext() { - ((NettyRemotingServer) remotingServer).loadSslContext(); - ((NettyRemotingServer) fastRemotingServer).loadSslContext(); - } - }); - } catch (Exception e) { - log.warn("FileWatchService created error, can't load the certificate dynamically"); - } - } - initialTransaction(); - initialAcl(); - initialRpcHooks(); - } - return result;Broker 启动过程
-贴代码
-
+ Locked ownable synchronizers: + - None -public void start() throws Exception { - if (this.messageStore != null) { - this.messageStore.start(); - } + Locked ownable synchronizers: + - None - if (this.remotingServer != null) { - this.remotingServer.start(); - } +"C1 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007fc9df0ce000 nid=0x4203 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE - if (this.fastRemotingServer != null) { - this.fastRemotingServer.start(); - } + Locked ownable synchronizers: + - None - if (this.fileWatchService != null) { - this.fileWatchService.start(); - } +"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007fc9de0a3800 nid=0x3503 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE - if (this.brokerOuterAPI != null) { - this.brokerOuterAPI.start(); - } + Locked ownable synchronizers: + - None - if (this.pullRequestHoldService != null) { - this.pullRequestHoldService.start(); - } +"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007fc9de89b000 nid=0x3403 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE - if (this.clientHousekeepingService != null) { - this.clientHousekeepingService.start(); - } + Locked ownable synchronizers: + - None - if (this.filterServerManager != null) { - this.filterServerManager.start(); - } +"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007fc9df0ca000 nid=0x3303 runnable [0x0000700005468000] + java.lang.Thread.State: RUNNABLE + at java.net.SocketInputStream.socketRead0(Native Method) + at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) + at java.net.SocketInputStream.read(SocketInputStream.java:171) + at java.net.SocketInputStream.read(SocketInputStream.java:141) + at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) + at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) + at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) + - locked <0x00000006c001b760> (a java.io.InputStreamReader) + at java.io.InputStreamReader.read(InputStreamReader.java:184) + at java.io.BufferedReader.fill(BufferedReader.java:161) + at java.io.BufferedReader.readLine(BufferedReader.java:324) + - locked <0x00000006c001b760> (a java.io.InputStreamReader) + at java.io.BufferedReader.readLine(BufferedReader.java:389) + at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64) - if (!messageStoreConfig.isEnableDLegerCommitLog()) { - startProcessorByHa(messageStoreConfig.getBrokerRole()); - handleSlaveSynchronize(messageStoreConfig.getBrokerRole()); - this.registerBrokerAll(true, false, true); - } + Locked ownable synchronizers: + - None - this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { +"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fc9de824000 nid=0x4503 runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE - @Override - public void run() { - try { - BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister()); - } catch (Throwable e) { - log.error("registerBrokerAll Exception", e); - } - } - }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS); + Locked ownable synchronizers: + - None - if (this.brokerStatsManager != null) { - this.brokerStatsManager.start(); - } +"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fc9dd811800 nid=0x4f03 in Object.wait() [0x0000700005262000] + java.lang.Thread.State: WAITING (on object monitor) + at java.lang.Object.wait(Native Method) + - waiting on <0x00000006c0008348> (a java.lang.ref.ReferenceQueue$Lock) + at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) + - locked <0x00000006c0008348> (a java.lang.ref.ReferenceQueue$Lock) + at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) + at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) - if (this.brokerFastFailure != null) { - this.brokerFastFailure.start(); - } + Locked ownable synchronizers: + - None +"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fc9de02a000 nid=0x5003 in Object.wait() [0x000070000515f000] + java.lang.Thread.State: WAITING (on object monitor) + at java.lang.Object.wait(Native Method) + - waiting on <0x00000006c001b940> (a java.lang.ref.Reference$Lock) + at java.lang.Object.wait(Object.java:502) + at java.lang.ref.Reference.tryHandlePending(Reference.java:191) + - locked <0x00000006c001b940> (a java.lang.ref.Reference$Lock) + at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) - }首先是启动messageStore,调用 start 方法,这里面又调用了一些代码
-
+JNI global references: 1087 +Found one Java-level deadlock: +============================= +"mythread2": + waiting for ownable synchronizer 0x000000076f5d4330, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), + which is held by "mythread1" +"mythread1": + waiting for ownable synchronizer 0x000000076f5d4360, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), + which is held by "mythread2" -public void start() throws Exception { +"VM Thread" os_prio=31 tid=0x00007fc9df00b800 nid=0x2c03 runnable - lock = lockFile.getChannel().tryLock(0, 1, false); - if (lock == null || lock.isShared() || !lock.isValid()) { - throw new RuntimeException("Lock failed,MQ already started"); - } +"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fc9de805000 nid=0x1e07 runnable - lockFile.getChannel().write(ByteBuffer.wrap("lock".getBytes())); - lockFile.getChannel().force(true); - { - /** - * 1. Make sure the fast-forward messages to be truncated during the recovering according to the max physical offset of the commitlog; - * 2. DLedger committedPos may be missing, so the maxPhysicalPosInLogicQueue maybe bigger that maxOffset returned by DLedgerCommitLog, just let it go; - * 3. Calculate the reput offset according to the consume queue; - * 4. Make sure the fall-behind messages to be dispatched before starting the commitlog, especially when the broker role are automatically changed. - */ - long maxPhysicalPosInLogicQueue = commitLog.getMinOffset(); - for (ConcurrentMap<Integer, ConsumeQueue> maps : this.consumeQueueTable.values()) { - for (ConsumeQueue logic : maps.values()) { - if (logic.getMaxPhysicOffset() > maxPhysicalPosInLogicQueue) { - maxPhysicalPosInLogicQueue = logic.getMaxPhysicOffset(); - } - } - } - if (maxPhysicalPosInLogicQueue < 0) { - maxPhysicalPosInLogicQueue = 0; - } - if (maxPhysicalPosInLogicQueue < this.commitLog.getMinOffset()) { - maxPhysicalPosInLogicQueue = this.commitLog.getMinOffset(); - /** - * This happens in following conditions: - * 1. If someone removes all the consumequeue files or the disk get damaged. - * 2. Launch a new broker, and copy the commitlog from other brokers. - * - * All the conditions has the same in common that the maxPhysicalPosInLogicQueue should be 0. - * If the maxPhysicalPosInLogicQueue is gt 0, there maybe something wrong. - */ - log.warn("[TooSmallCqOffset] maxPhysicalPosInLogicQueue={} clMinOffset={}", maxPhysicalPosInLogicQueue, this.commitLog.getMinOffset()); - } - log.info("[SetReputOffset] maxPhysicalPosInLogicQueue={} clMinOffset={} clMaxOffset={} clConfirmedOffset={}", - maxPhysicalPosInLogicQueue, this.commitLog.getMinOffset(), this.commitLog.getMaxOffset(), this.commitLog.getConfirmOffset()); - this.reputMessageService.setReputFromOffset(maxPhysicalPosInLogicQueue); - this.reputMessageService.start(); +"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fc9de003800 nid=0x2a03 runnable - /** - * 1. Finish dispatching the messages fall behind, then to start other services. - * 2. DLedger committedPos may be missing, so here just require dispatchBehindBytes <= 0 - */ - while (true) { - if (dispatchBehindBytes() <= 0) { - break; - } - Thread.sleep(1000); - log.info("Try to finish doing reput the messages fall behind during the starting, reputOffset={} maxOffset={} behind={}", this.reputMessageService.getReputFromOffset(), this.getMaxPhyOffset(), this.dispatchBehindBytes()); - } - this.recoverTopicQueueTable(); - } +"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fc9df002000 nid=0x5403 runnable - if (!messageStoreConfig.isEnableDLegerCommitLog()) { - this.haService.start(); - this.handleScheduleMessageService(messageStoreConfig.getBrokerRole()); - } +"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fc9df002800 nid=0x5203 runnable - this.flushConsumeQueueService.start(); - this.commitLog.start(); - this.storeStatsService.start(); +"VM Periodic Task Thread" os_prio=31 tid=0x00007fc9df11a800 nid=0x3a03 waiting on condition - this.createTempFile(); - this.addScheduleTask(); - this.shutdown = false; - }调用DefaultMessageStore.start方法启动DefaultMessageStore对象中的一些服务线程。
--
-
- 启动ReputMessageService服务线程 -
- 启动FlushConsumeQueueService服务线程; -
- 调用CommitLog.start方法,启动CommitLog对象中的FlushCommitLogService线程服务,若是同步刷盘(SYNC_FLUSH)则是启动GroupCommitService线程服务;若是异步刷盘(ASYNC_FLUSH)则是启动FlushRealTimeService线程服务; -
- 启动StoreStatsService线程服务; -
- 启动定时清理任务 -
然后是启动ClientHousekeepingService的 netty 服务端和客户端,然后是启动fileWatchService证书服务,接着启动BrokerOuterAPI中的NettyRemotingClient,即建立与NameServer的链接,用于自身Broker与其他模块的RPC功能调用;包括获取NameServer的地址、注册Broker、注销Broker、获取Topic配置、获取消息进度信息、获取订阅关系等RPC功能,然后是PullRequestHoldService服务线程,这个就是实现长轮询的,然后启动管家ClientHousekeepingService服务,负责扫描不活跃的生产者,消费者和 filter,启动FilterServerManager 过滤器服务管理,然后启动定时任务调用org.apache.rocketmq.broker.BrokerController#registerBrokerAll向所有 nameserver 注册 broker,最后是按需开启org.apache.rocketmq.store.stats.BrokerStatsManager和org.apache.rocketmq.broker.latency.BrokerFastFailure,基本上启动过程就完成了
+Java stack information for the threads listed above: +=================================================== +"mythread2": + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f5d4330> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) + at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) + at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) + at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) + at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) + at com.nicksxs.thread_dump_demo.ThreadDumpDemoApplication$2.run(ThreadDumpDemoApplication.java:34) +"mythread1": + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000076f5d4360> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) + at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) + at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) + at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) + at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) + at com.nicksxs.thread_dump_demo.ThreadDumpDemoApplication$1.run(ThreadDumpDemoApplication.java:22) + +Found 1 deadlock.前面的信息其实上次就看过了,后面就可以发现有个死锁了,
+![]()
上面比较长,把主要的截出来,就是这边的,这点就很强大了。jmap
惯例还是看一下帮助信息
+![]()
这个相对命令比较多,不过因为现在 dump 下来我们可能会用文件模式,然后将文件下载下来使用 mat 进行分析,所以可以使用jmap -dump:live,format=b,file=heap.bin <pid>
命令照着上面看的就是打印活着的对象,然后以二进制格式,文件名叫 heap.bin 然后最后就是进程 id,打印出来以后可以用 mat 打开![]()
这样就可以很清晰的看到应用里的各种信息,jmap 直接在命令中还可以看很多信息,比如使用jmap -histo <pid>打印对象的实例数和对象占用的内存![]()
jmap -finalizerinfo <pid>打印正在等候回收的对象![]()
小tips
对于一些应用内存已经占满了,jstack 和 jmap 可能会连不上的情况,可以使用
]]>-F参数强制打印线程或者 dump 文件,但是要注意这两者使用的用户必须与 java 进程启动用户一致,并且使用的 jdk 也要一致- MQ -RocketMQ -消息队列 -RocketMQ -中间件 -RocketMQ +Java +Thread dump +问题排查 +工具 - MQ -消息队列 -RocketMQ -削峰填谷 -中间件 -源码解析 -Broker +Java +JPS +JStack +JMap @@ -12778,36 +12778,7 @@ result = result .printStackTrace(); } } - -看下查询结果
-]]> -![]()
- -Java -- -Java -Sharding-Jdbc -- 聊聊 Sharding-Jdbc 的简单原理初篇 -/2021/12/26/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E5%8E%9F%E7%90%86%E5%88%9D%E7%AF%87/ -在上一篇 sharding-jdbc 的介绍中其实碰到过一个问题,这里也引出了一个比较有意思的话题
就是我在执行 query 的时候犯过一个比较难发现的错误, -
-ResultSet resultSet = ps.executeQuery(sql);实际上应该是
-
-ResultSet resultSet = ps.executeQuery();而这里的差别就是,是否传 sql 这个参数,首先我们要知道这个 ps 是什么,它也是个接口
-java.sql.PreparedStatement,而真正的实现类是org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement,我们来看下继承关系![]()
这里可以看到继承关系里有org.apache.shardingsphere.driver.jdbc.unsupported.AbstractUnsupportedOperationPreparedStatement
那么在我上面的写错的代码里
-@Override -public final ResultSet executeQuery(final String sql) throws SQLException { - throw new SQLFeatureNotSupportedException("executeQuery with SQL for PreparedStatement"); -}这个报错一开始让我有点懵,后来点进去了发现是这么个异常,但是我其实一开始是用的更新语句,以为更新不支持,因为平时使用没有深究过,以为是不是需要使用 Mybatis 才可以执行更新,但是理论上也不应该,再往上看原来这些异常是由 sharding-jdbc 包装的,也就是在上面说的
-AbstractUnsupportedOperationPreparedStatement,这其实也是一种设计思想,本身 jdbc 提供了一系列接口,由各家去支持,包括 mysql,sql server,oracle 等,而正因为这个设计,所以 sharding-jdbc 也可以在此基础上进行设计,我们可以总体地看下 sharding-jdbc 的实现基础![]()
看了前面ShardingSpherePreparedStatement的继承关系,应该也能猜到这里的几个类都是实现了 jdbc 的基础接口,![]()
在前一篇的 demo 中的
-Connection conn = dataSource.getConnection();其实就获得了
-org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection#ShardingSphereConnection
然后获得java.sql.PreparedStatement
-PreparedStatement ps = conn.prepareStatement(sql)就是获取了
-org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement
然后就是执行
-ResultSet resultSet = ps.executeQuery();然后获得结果
-org.apache.shardingsphere.driver.jdbc.core.resultset.ShardingSphereResultSet其实像 mybatis 也是基于这样去实现的
+看下查询结果
]]>![]()
Java @@ -12862,8 +12833,8 @@ result = result @@ -12954,34 +12925,32 @@ void ReadView::prepare(trx_id_t id) {Java -Dubbo - 线程池 Dubbo +Dubbo - 线程池 线程池 ThreadPool - 聊聊 redis 缓存的应用问题 -/2021/01/31/%E8%81%8A%E8%81%8A-redis-%E7%BC%93%E5%AD%98%E7%9A%84%E5%BA%94%E7%94%A8%E9%97%AE%E9%A2%98/ -前面写过一系列的 redis 源码分析的,但是实际上很多的问题还是需要结合实际的使用,然后其实就避不开缓存使用的三个著名问题,穿透,击穿和雪崩,这三个概念也是有着千丝万缕的关系, - 缓存穿透
缓存穿透是指当数据库中本身就不存在这个数据的时候,使用一般的缓存策略时访问不到缓存后就访问数据库,但是因为数据库也没数据,所以如果不做任何策略优化的话,这类数据就每次都会访问一次数据库,对数据库压力也会比较大。
-缓存击穿
缓存击穿跟穿透比较类似的,都是访问缓存不在,然后去访问数据库,与穿透不一样的是击穿是在数据库中存在数据,但是可能由于第一次访问,或者缓存过期了,需要访问到数据库,这对于访问量小的情况其实算是个正常情况,但是随着请求量变高就会引发一些性能隐患。
-缓存雪崩
缓存雪崩就是击穿的大规模集群效应,当大量的缓存过期失效的时候,这些请求都是直接访问到数据库了,会对数据库造成很大的压力。
-对于以上三种场景也有一些比较常见的解决方案,但也不能说是万无一失的,需要随着业务去寻找合适的方案
-解决缓存穿透
对于数据库中就没这个数据的时候,一种是可以对这个 key 设置下空值,即以一个特定的表示是数据库不存在的,这种情况需要合理地调整过期时间,当这个 key 在数据库中有数据了的话,也需要有策略去更新这个值,并且如果这类 key 非常多,这个方法就会不太合适,就可以使用第二种方法,就是布隆过滤器,bloom filter,前置一个布隆过滤器,当这个 key 在数据库不存在的话,先用布隆过滤器挡一道,如果不在的话就直接返回了,当然布隆过滤器不是绝对的准确的
-解决缓存击穿
当一个 key 的缓存过期了,如果大量请求过来访问这个 key,请求都会落在数据库里,这个时候就可以使用一些类似于互斥锁的方式去让一个线程去访问数据库,更新缓存,但是这里其实也有个问题,就是如果是热点 key 其实这种方式也比较危险,万一更新失败,或者更新操作的时候耗时比较久,就会有一大堆请求卡在那,这种情况可能需要有一些异步提前刷新缓存,可以结合具体场景选择方式
-解决缓存雪崩
雪崩的情况是指大批量的 key 都一起过期了,击穿的放大版,大批量的请求都打到数据库上了,一方面有可能直接缓存不可用了,就需要用集群化高可用的缓存服务,然后对于实际使用中也可以使用本地缓存结合 redis 缓存,去提高可用性,再配合一些限流措施,然后就是缓存使用过程总的过期时间最好能加一些随机值,防止在同一时间过期而导致雪崩,结合互斥锁防止大量请求打到数据库。
+聊聊 Sharding-Jdbc 的简单原理初篇 +/2021/12/26/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E5%8E%9F%E7%90%86%E5%88%9D%E7%AF%87/ +在上一篇 sharding-jdbc 的介绍中其实碰到过一个问题,这里也引出了一个比较有意思的话题
就是我在执行 query 的时候犯过一个比较难发现的错误, +
+ResultSet resultSet = ps.executeQuery(sql);实际上应该是
+
+ResultSet resultSet = ps.executeQuery();而这里的差别就是,是否传 sql 这个参数,首先我们要知道这个 ps 是什么,它也是个接口
+java.sql.PreparedStatement,而真正的实现类是org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement,我们来看下继承关系![]()
这里可以看到继承关系里有org.apache.shardingsphere.driver.jdbc.unsupported.AbstractUnsupportedOperationPreparedStatement
那么在我上面的写错的代码里
+@Override +public final ResultSet executeQuery(final String sql) throws SQLException { + throw new SQLFeatureNotSupportedException("executeQuery with SQL for PreparedStatement"); +}这个报错一开始让我有点懵,后来点进去了发现是这么个异常,但是我其实一开始是用的更新语句,以为更新不支持,因为平时使用没有深究过,以为是不是需要使用 Mybatis 才可以执行更新,但是理论上也不应该,再往上看原来这些异常是由 sharding-jdbc 包装的,也就是在上面说的
+AbstractUnsupportedOperationPreparedStatement,这其实也是一种设计思想,本身 jdbc 提供了一系列接口,由各家去支持,包括 mysql,sql server,oracle 等,而正因为这个设计,所以 sharding-jdbc 也可以在此基础上进行设计,我们可以总体地看下 sharding-jdbc 的实现基础![]()
看了前面ShardingSpherePreparedStatement的继承关系,应该也能猜到这里的几个类都是实现了 jdbc 的基础接口,![]()
在前一篇的 demo 中的
+Connection conn = dataSource.getConnection();其实就获得了
+org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection#ShardingSphereConnection
然后获得java.sql.PreparedStatement
+PreparedStatement ps = conn.prepareStatement(sql)就是获取了
+org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement
然后就是执行
+ResultSet resultSet = ps.executeQuery();然后获得结果
+org.apache.shardingsphere.driver.jdbc.core.resultset.ShardingSphereResultSet其实像 mybatis 也是基于这样去实现的
]]>- Redis -应用 -缓存 -缓存 -穿透 -击穿 -雪崩 +Java - Redis -缓存穿透 -缓存击穿 -缓存雪崩 -布隆过滤器 -bloom filter -互斥锁 +Java +Sharding-Jdbc @@ -13064,6 +13033,96 @@ constexpr size_t DATA_ROLL_PTR_LEN read view ++ 聊聊 Sharding-Jdbc 分库分表下的分页方案 +/2022/01/09/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8%E4%B8%8B%E7%9A%84%E5%88%86%E9%A1%B5%E6%96%B9%E6%A1%88/ +前面在聊 Sharding-Jdbc 的时候看到了一篇文章,关于一个分页的查询,一直比较直接的想法就是分库分表下的分页是非常不合理的,一般我们的实操方案都是分表加上 ES 搜索做分页,或者通过合表读写分离的方案,因为对于 sharding-jdbc 如果没有带分表键,查询基本都是只能在所有分表都执行一遍,然后再加上分页,基本上是分页越大后续的查询越耗资源,但是仔细的去想这个细节还是这次,就简单说说 +
首先就是我的分表结构 +
+CREATE TABLE `student_time_0` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `name` varchar(200) COLLATE utf8_bin DEFAULT NULL, + `age` tinyint(3) unsigned DEFAULT NULL, + `create_time` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=674 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;有这样的三个表,
+student_time_0,student_time_1,student_time_2, 以 user_id 作为分表键,根据表数量取模作为分表依据
这里先构造点数据,
+insert into student_time (`name`, `user_id`, `age`, `create_time`) values (?, ?, ?, ?)主要是为了保证
+create_time唯一比较好说明问题,
+int i = 0; +try ( + Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(insertSql)) { + do { + ps.setString(1, localName + new Random().nextInt(100)); + ps.setLong(2, 10086L + (new Random().nextInt(100))); + ps.setInt(3, 18); + ps.setLong(4, new Date().getTime()); + + + int result = ps.executeUpdate(); + LOGGER.info("current execute result: {}", result); + Thread.sleep(new Random().nextInt(100)); + i++; + } while (i <= 2000);三个表的数据分别是 673,678,650,说明符合预期了,各个表数据不一样,接下来比如我们想要做一个这样的分页查询
+
+select * from student_time ORDER BY create_time ASC limit 1000, 5;
+student_time对于我们使用的sharding-jdbc来说当然是逻辑表,首先从一无所知去想这个查询如果我们自己来处理应该是怎么做,
首先是不是可以每个表都从 333 开始取 5 条数据,类似于下面的查询,然后进行 15 条的合并重排序获取前面的 5 条
+select * from student_time_0 ORDER BY create_time ASC limit 333, 5; +select * from student_time_1 ORDER BY create_time ASC limit 333, 5; +select * from student_time_2 ORDER BY create_time ASC limit 333, 5;忽略前面 limit 差的 1,这个结果除非三个表的分布是绝对的均匀,否则结果肯定会出现一定的偏差,以为每个表的 333 这个位置对于其他表来说都不一定是一样的,这样对于最后整体的结果,就会出现偏差
+
因为一直在纠结怎么让这个更直观的表现出来,所以尝试画了个图![]()
黑色的框代表我从每个表里按排序从 334 到 338 的 5 条数据, 他们在每个表里都是代表了各自正确的排序值,但是对于我们想要的其实是合表后的 1001,1005 这五条,然后我们假设总的排序值位于前 1000 的分布是第 0 个表是 320 条,第 1 个表是 340 条,第 2 个表是 340 条,那么可以明显地看出来我这么查的结果简单合并肯定是不对的。
那么 sharding-jdbc 是如何保证这个结果的呢,其实就是我在每个表里都查分页偏移量和分页大小那么多的数据,在我这个例子里就是对于 0,1,2 三个分表每个都查 1005 条数据,即使我的数据不平衡到最极端的情况,前 1005 条数据都出在某个分表中,也可以正确获得最后的结果,但是明显的问题就是大分页,数据较多,就会导致非常大的问题,即使如 sharding-jdbc 对于合并排序的优化做得比较好,也还是需要传输那么大量的数据,并且查询也耗时,那么有没有解决方案呢,应该说有两个,或者说主要是想讲后者
第一个办法是像这种查询,如果业务上不需要进行跳页,而是只给下一页,那么我们就能把前一次的最大偏移量的 create_time 记录下来,下一页就可以拿着这个偏移量进行查询,这个比较简单易懂,就不多说了
第二个办法是看的58 沈剑的一篇文章,尝试理解讲述一下,
这个办法的第一步跟前面那个错误的方法或者说不准确的方法一样,先是将分页偏移量平均后在三个表里进行查询
+t0 +334 10158 nick95 18 1641548941767 +335 10098 nick11 18 1641548941879 +336 10167 nick51 18 1641548942089 +337 10167 nick3 18 1641548942119 +338 10170 nick57 18 1641548942169 + + +t1 +334 10105 nick98 18 1641548939071 最小 +335 10174 nick94 18 1641548939377 +336 10129 nick85 18 1641548939442 +337 10141 nick84 18 1641548939480 +338 10096 nick74 18 1641548939668 + +t2 +334 10184 nick11 18 1641548945075 +335 10109 nick93 18 1641548945382 +336 10181 nick41 18 1641548945583 +337 10130 nick80 18 1641548945993 +338 10184 nick19 18 1641548946294 最大然后要做什么呢,其实目标比较明白,因为前面那种方法其实就是我知道了前一页的偏移量,所以可以直接当做条件来进行查询,那这里我也想着拿到这个条件,所以我将第一遍查出来的最小的 create_time 和最大的 create_time 找出来,然后再去三个表里查询,其实主要是最小值,因为我拿着最小值去查以后我就能知道这个最小值在每个表里处在什么位置,
+
+t0 +322 10161 nick81 18 1641548939284 +323 10113 nick16 18 1641548939393 +324 10110 nick56 18 1641548939577 +325 10116 nick69 18 1641548939588 +326 10173 nick51 18 1641548939646 + +t1 +334 10105 nick98 18 1641548939071 +335 10174 nick94 18 1641548939377 +336 10129 nick85 18 1641548939442 +337 10141 nick84 18 1641548939480 +338 10096 nick74 18 1641548939668 + +t2 +297 10136 nick28 18 1641548939161 +298 10142 nick68 18 1641548939177 +299 10124 nick41 18 1641548939237 +300 10148 nick87 18 1641548939510 +301 10169 nick23 18 1641548939715我只贴了前五条数据,为了方便知道偏移量,每个分表都使用了自增主键,我们可以看到前一次查询的最小值分别在其他两个表里的位置分别是 322-1 和 297-1,那么对于总体来说这个时间应该是在
+]]>322 - 1 + 333 + 297 - 1 = 951,那这样子我只要对后面的数据最多每个表查1000 - 951 + 5 = 54条数据再进行合并排序就可以获得最终正确的结果。
这个就是传说中的二次查询法。+ +Java ++ +Java +Sharding-Jdbc + -聊聊 mysql 索引的一些细节 /2020/12/27/%E8%81%8A%E8%81%8A-mysql-%E7%B4%A2%E5%BC%95%E7%9A%84%E4%B8%80%E4%BA%9B%E7%BB%86%E8%8A%82/ @@ -13074,134 +13133,90 @@ constexpr size_t DATA_ROLL_PTR_LEN `null_key1` varchar(255) DEFAULT NULL, `null_key2` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), - KEY `idx_1` (`null_key`) USING BTREE, - KEY `idx_2` (`null_key1`) USING BTREE, - KEY `idx_3` (`null_key2`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -用个存储过程来插入数据
-
--delimiter $ #以delimiter来标记用$表示存储过程结束 -create procedure nullIndex1() -begin -declare i int; -declare j int; -set i=1; -set j=1; -while(i<=100) do - while(j<=100) do - IF (i % 3 = 0) THEN - INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (null , LEFT(MD5(RAND()), 8), LEFT(MD5(RAND()), 8)); - ELSEIF (i % 3 = 1) THEN - INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (LEFT(MD5(RAND()), 8), NULL, LEFT(MD5(RAND()), 8)); - ELSE - INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (LEFT(MD5(RAND()), 8), LEFT(MD5(RAND()), 8), NULL); - END IF; - set j=j+1; - end while; - set i=i+1; - set j=1; -end while; -end -$ -call nullIndex1();然后看下我们的 is null 查询
-
-EXPLAIN select * from null_index_t WHERE null_key is null;
-![]()
再来看看另一个
-EXPLAIN select * from null_index_t WHERE null_key is not null;
-![]()
从这里能看出来啥呢,可以思考下从上面可以发现,
-is null应该是用上了索引了,所以至少不是一刀切不能用,但是看着is not null好像不太行额
我们在做一点小改动,把这个表里的数据改成 9100 条是 null,剩下 900 条是有值的,然后再执行下![]()
然后再来看看执行结果
-EXPLAIN select * from null_index_t WHERE null_key is null;
-![]()
-EXPLAIN select * from null_index_t WHERE null_key is not null;
-]]> -![]()
是不是不一样了,这里再补充下我试验使用的 mysql 是 5.7 的,不保证在其他版本的一致性,
其实可以看出随着数据量的变化,mysql 会不会使用索引是会变化的,不是说 is not null 一定会使用,也不是一定不会使用,而是优化器会根据查询成本做个预判,这个预判尽可能会减小查询成本,主要包括回表啥的,但是也不一定完全准确。- -Mysql -C -索引 -Mysql -- -mysql -索引 -is null -is not null -procedure -- +聊聊Java中的单例模式 -/2019/12/21/%E8%81%8A%E8%81%8AJava%E4%B8%AD%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/ -这是个 Java 面试的高频问题,我也遇到过,以往都是觉得这类题没意思,网上一搜一大堆,也不愿意记,其实说回来,主要还是没静下心来好好去理解,今天无意中看到一个课程,基本帮我把一些疑惑的点讲清楚了,首先单例是啥意思,这个其实是有范围一说,比如我起了个 Spring Boot应用,在这个应用范围内,我的常规 bean 是单例的,意味着 getBean 的时候其实永远只会拿到那一个对象,那要怎么来写一个单例呢,首先就是传说中的饿汉模式,也是最简单的 -饿汉模式
-public class Singleton1 { - // 首先,将构造方法变成私有的 - private Singleton1() {}; - // 创建私有静态实例,这样第一次使用的时候就会进行创建 - private static Singleton instance = new Singleton1(); - - // 使用这个对象都是通过这个 getInstance 来获取 - public static Singleton1 getInstance() { - return instance; - } - // 瞎写一个静态方法。这里想说的是,如果我们只是要调用 Singleton.getDate(...), - // 本来是不想要生成 Singleton 实例的,不过没办法,已经生成了 - public static Date getDate(String mode) {return new Date();} -}上面借鉴了一些代码,其实这是最基本,也不会错的方法,但是正如其中
-getDate方法里说的问题,有时候并没有想那这个对象,但是因为我调用了这个类的静态方法,导致对象已经生成了,可能这也是饿汉模式名字的来由,不管三七二十一给你生成个单例就完事了,不管有没有用,但是这种个人觉得也没啥大问题,如果是面试的话最好说出来它的缺点饱汉模式
-public class Singleton2 { - // 首先,也是先堵死 new Singleton() 这条路,将构造方法变成私有 - private Singleton2() {} - // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的 - private static volatile Singleton2 instance = null; - - private int m = 9; - - public static Singleton getInstance() { - if (instance == null) { - // 加锁 - synchronized (Singleton2.class) { - // 这一次判断也是必须的,不然会有并发问题 - if (instance == null) { - instance = new Singleton2(); - } - } - } - return instance; - } -}这里容易错的有三点,理解了其实就比较好记了
-第一点,为啥不在 getInstance 上整个代码块加
-synchronized,这个其实比较容易理解,就是锁的力度太大,性能太差了,这点其实也要去理解,可以举个夸张的例子,比如我一个电商的服务,如果为了避免一个人的订单出现问题,是不是可以从请求入口就把他锁住,到请求结束释放,那么里面做的事情都有保障,然而这显然不可能,因为我们想要这种竞态条件抢占资源的时间尽量减少,防止其他线程等待。
第二点,为啥synchronized之已经检查了instance == null,还要在里面再检查一次,这个有个术语,叫double check lock,但是为啥要这么做呢,其实很简单,想象当有两个线程,都过了第一步为空判断,这个时候只有一个线程能拿到这个锁,另一个线程就等待了,如果不再判断一次,那么第一个线程新建完对象释放锁之后,第二个线程又能拿到锁,再去创建一个对象。
第三点,为啥要volatile关键字,原先对它的理解是它修饰的变量在 JMM 中能及时将变量值写到主存中,但是它还有个很重要的作用,就是防止指令重排序,instance = new Singleton();这行代码其实在底层是分成三条指令执行的,第一条是在堆上申请了一块内存放这个对象,但是对象的字段啥的都还是默认值,第二条是设置对象的值,比如上面的 m 是 9,然后第三条是将这个对象和虚拟机栈上的指针建立引用关联,那么如果我不用volatile关键字,这三条指令就有可能出现重排,比如变成了 1-3-2 这种顺序,当执行完第二步时,有个线程来访问这个对象了,先判断是不是空,发现不是空的,就拿去直接用了,是不是就出现问题了,所以这个volatile也是不可缺少的嵌套类
-public class Singleton3 { - - private Singleton3() {} - // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性 - private static class Holder { - private static Singleton3 instance = new Singleton3(); - } - public static Singleton3 getInstance() { - return Holder.instance; - } -}这个我个人感觉是饿汉模式的升级版,可以在调用
-getInstance的时候去实例化对象,也是比较推荐的枚举单例
-public enum Singleton { - INSTANCE; - - public void doSomething(){ - //todo doSomething - } -}枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。
+ KEY `idx_1` (`null_key`) USING BTREE, + KEY `idx_2` (`null_key1`) USING BTREE, + KEY `idx_3` (`null_key2`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +用个存储过程来插入数据
+
++delimiter $ #以delimiter来标记用$表示存储过程结束 +create procedure nullIndex1() +begin +declare i int; +declare j int; +set i=1; +set j=1; +while(i<=100) do + while(j<=100) do + IF (i % 3 = 0) THEN + INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (null , LEFT(MD5(RAND()), 8), LEFT(MD5(RAND()), 8)); + ELSEIF (i % 3 = 1) THEN + INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (LEFT(MD5(RAND()), 8), NULL, LEFT(MD5(RAND()), 8)); + ELSE + INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (LEFT(MD5(RAND()), 8), LEFT(MD5(RAND()), 8), NULL); + END IF; + set j=j+1; + end while; + set i=i+1; + set j=1; +end while; +end +$ +call nullIndex1();然后看下我们的 is null 查询
+
+EXPLAIN select * from null_index_t WHERE null_key is null;
+![]()
再来看看另一个
+EXPLAIN select * from null_index_t WHERE null_key is not null;
+![]()
从这里能看出来啥呢,可以思考下从上面可以发现,
+is null应该是用上了索引了,所以至少不是一刀切不能用,但是看着is not null好像不太行额
我们在做一点小改动,把这个表里的数据改成 9100 条是 null,剩下 900 条是有值的,然后再执行下![]()
然后再来看看执行结果
+EXPLAIN select * from null_index_t WHERE null_key is null;
+![]()
+EXPLAIN select * from null_index_t WHERE null_key is not null;
]]>![]()
是不是不一样了,这里再补充下我试验使用的 mysql 是 5.7 的,不保证在其他版本的一致性,
其实可以看出随着数据量的变化,mysql 会不会使用索引是会变化的,不是说 is not null 一定会使用,也不是一定不会使用,而是优化器会根据查询成本做个预判,这个预判尽可能会减小查询成本,主要包括回表啥的,但是也不一定完全准确。- Java -Design Patterns -Singleton +Mysql +C +索引 +Mysql - +设计模式 -Design Patterns -单例 -Singleton +mysql +索引 +is null +is not null +procedure ++ 聊聊 redis 缓存的应用问题 +/2021/01/31/%E8%81%8A%E8%81%8A-redis-%E7%BC%93%E5%AD%98%E7%9A%84%E5%BA%94%E7%94%A8%E9%97%AE%E9%A2%98/ +前面写过一系列的 redis 源码分析的,但是实际上很多的问题还是需要结合实际的使用,然后其实就避不开缓存使用的三个著名问题,穿透,击穿和雪崩,这三个概念也是有着千丝万缕的关系, + +缓存穿透
缓存穿透是指当数据库中本身就不存在这个数据的时候,使用一般的缓存策略时访问不到缓存后就访问数据库,但是因为数据库也没数据,所以如果不做任何策略优化的话,这类数据就每次都会访问一次数据库,对数据库压力也会比较大。
+缓存击穿
缓存击穿跟穿透比较类似的,都是访问缓存不在,然后去访问数据库,与穿透不一样的是击穿是在数据库中存在数据,但是可能由于第一次访问,或者缓存过期了,需要访问到数据库,这对于访问量小的情况其实算是个正常情况,但是随着请求量变高就会引发一些性能隐患。
+缓存雪崩
缓存雪崩就是击穿的大规模集群效应,当大量的缓存过期失效的时候,这些请求都是直接访问到数据库了,会对数据库造成很大的压力。
+对于以上三种场景也有一些比较常见的解决方案,但也不能说是万无一失的,需要随着业务去寻找合适的方案
+解决缓存穿透
对于数据库中就没这个数据的时候,一种是可以对这个 key 设置下空值,即以一个特定的表示是数据库不存在的,这种情况需要合理地调整过期时间,当这个 key 在数据库中有数据了的话,也需要有策略去更新这个值,并且如果这类 key 非常多,这个方法就会不太合适,就可以使用第二种方法,就是布隆过滤器,bloom filter,前置一个布隆过滤器,当这个 key 在数据库不存在的话,先用布隆过滤器挡一道,如果不在的话就直接返回了,当然布隆过滤器不是绝对的准确的
+解决缓存击穿
当一个 key 的缓存过期了,如果大量请求过来访问这个 key,请求都会落在数据库里,这个时候就可以使用一些类似于互斥锁的方式去让一个线程去访问数据库,更新缓存,但是这里其实也有个问题,就是如果是热点 key 其实这种方式也比较危险,万一更新失败,或者更新操作的时候耗时比较久,就会有一大堆请求卡在那,这种情况可能需要有一些异步提前刷新缓存,可以结合具体场景选择方式
+解决缓存雪崩
雪崩的情况是指大批量的 key 都一起过期了,击穿的放大版,大批量的请求都打到数据库上了,一方面有可能直接缓存不可用了,就需要用集群化高可用的缓存服务,然后对于实际使用中也可以使用本地缓存结合 redis 缓存,去提高可用性,再配合一些限流措施,然后就是缓存使用过程总的过期时间最好能加一些随机值,防止在同一时间过期而导致雪崩,结合互斥锁防止大量请求打到数据库。
+]]>+ +Redis +应用 +缓存 +缓存 +穿透 +击穿 +雪崩 ++ Redis +缓存穿透 +缓存击穿 +缓存雪崩 +布隆过滤器 +bloom filter +互斥锁 @@ -13554,6 +13569,88 @@ $ +AutoConfiguration + +聊聊一次 brew update 引发的血案 +/2020/06/13/%E8%81%8A%E8%81%8A%E4%B8%80%E6%AC%A1-brew-update-%E5%BC%95%E5%8F%91%E7%9A%84%E8%A1%80%E6%A1%88/ +熟悉我的人(谁熟悉你啊🙄)知道我以前写过 PHP,虽然现在在工作中没用到了,但是自己的一些小工具还是会用 PHP 来写,但是在 Mac 碰到了一个环境相关的问题,因为我也是个更新狂魔,用了 brew 之后因为 gfw 的原因,如果长时间不更新,有时候要装一个用它装一个软件的话,前置的更新耗时就会让人非常头大,所以我基本会隔天 update 一下,但是这样会带来一个很心烦的问题,就是像这样,因为我是要用一个固定版本的 PHP,如果一直升需要一直配扩展啥的也很麻烦,如果一直升级 PHP 到最新版可能会比较少碰到这个问题 + +
+dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.64.dylib这是什么鬼啊,然后我去这个目录下看了下,已经都是libicui18n.67.dylib了,而且它没有把原来的版本保留下来,首先这个是个叫 icu4c是啥玩意,谷歌了一下
++
+ICU4C是ICU在C/C++平台下的版本, ICU(International Component for Unicode)是基于”IBM公共许可证”的,与开源组织合作研究的, 用于支持软件国际化的开源项目。ICU4C提供了C/C++平台强大的国际化开发能力,软件开发者几乎可以使用ICU4C解决任何国际化的问题,根据各地的风俗和语言习惯,实现对数字、货币、时间、日期、和消息的格式化、解析,对字符串进行大小写转换、整理、搜索和排序等功能,必须一提的是,ICU4C提供了强大的BIDI算法,对阿拉伯语等BIDI语言提供了完善的支持。
+然后首先想到的解决方案就是能不能我使用
+brew install icu4c@64来重装下原来的版本,发现不行,并木有,之前的做法就只能是去网上把 64 的下载下来,然后放到这个目录,比较麻烦不智能,虽然没抱着希望在谷歌着,不过这次竟然给我找到了一个我认为非常 nice 的解决方案,因为是在 Stack Overflow 找到的,本着写给像我这样的小小白看的,那就稍微翻译一下
第一步,我们到 brew的目录下
+cd $(brew --prefix)/Homebrew/Library/Taps/homebrew/homebrew-core/Formula这个可以理解为是 maven 的 pom 文件,不过有很多不同之处,使用ruby 写的,然后一个文件对应一个组件或者软件,那我们看下有个叫icu4c.rb的文件,
+
第二步看看它的提交历史
+git log --follow icu4c.rb在 git log 的海洋中寻找,寻找它的(64版本)的身影
+![]()
第三步注意这三个红框,Stack Overflow 给出来的答案这一步是找到这个 commit id 直接切出一个新分支
+git checkout -b icu4c-63 e7f0f10dc63b1dc1061d475f1a61d01b70ef2cb7其实注意 commit id 旁边的红框,这个是有tag 的,可以直接
+
+git checkout icu4c-64PS: 因为我的问题是出在 64 的问题,Stack Overflow 回答的是 63 的,反正是一样的解决方法
+
第四部,切回去之后我们就可以用 brew 提供的基于文件的安装命令来重新装上 64 版本
+brew reinstall ./icu4c.rb然后就是第五步,切换版本
+
+brew switch icu4c 64.2最后把分支切回来
+
+git checkout master是不是感觉很厉害的解决方法,大佬还提供了一个更牛的,直接写个 zsh 方法
+
+# zsh +function hiicu64() { + local last_dir=$(pwd) + + cd $(brew --prefix)/Homebrew/Library/Taps/homebrew/homebrew-core/Formula + git checkout icu4c-4 + brew reinstall ./icu4c.rb + brew switch icu4c 64.2 + git checkout master + + cd $last_dir +}对应自己的版本改改版本号就可以了,非常好用。
+]]>+ +Mac +PHP +Homebrew +icu4c ++ +Mac +PHP +Homebrew +icu4c +zsh ++ 聊聊厦门旅游的好与不好 +/2021/04/11/%E8%81%8A%E8%81%8A%E5%8E%A6%E9%97%A8%E6%97%85%E6%B8%B8%E7%9A%84%E5%A5%BD%E4%B8%8E%E4%B8%8D%E5%A5%BD/ +这几天去了趟厦门,原来几年前就想去了,本来都请好假了,后面因为一些事情没去成,这次刚好公司组织,就跟 LD 一起去了厦门,也不洋洋洒洒地写游记了,后面可能会有,今天先来总结下好的地方和比较坑的地方。 +
这次主要去了中山路、鼓浪屿、曾厝(cuo)垵、植物园、灵玲马戏团,因为住的离环岛路比较近,还有幸现场看了下厦门马拉松,其中 +中山路
这里看上去是有点民国时期的建筑风格,部分像那种电视里的租界啥的,不过这次去的时候都在翻修,路一大半拦起来了,听导游说这里往里面走有个局口街,然后上次听前同事说厦门比较有名的就是沙茶面和海蛎煎,不出意料的不太爱吃,沙茶面比较普通,可能是没吃到正宗的,海蛎煎吃不惯,倒是有个大叔的沙茶里脊还不错,在局口街那,还有小哥在那拍,应该也算是个网红打卡点了,然后吃了个油条麻糍也还不错,总体如果是看建筑的话可能最近不是个好时间,个人也没这方面爱好,吃的话最好多打听打听沙茶面跟海蛎煎哪里正宗。如果不知道哪家好吃,也不爱看这类建筑的可以排个坑。
+鼓浪屿
鼓浪屿也是完全没啥概念,需要乘船过去,但是只要二十分钟,岛上没有机动车,基本都靠走,有几个比较有名的地方,菽庄花园,里面有钢琴博物馆,对这个感兴趣的可以去看看,旁边是沙滩还可以逛逛,然后有各种博物馆,风琴啥的,岛上最大的特色是巷子多,道听途说有三百多条小巷,还有几个网红打卡点,周杰伦晴天墙,还有个最美转角,都是挤满了人排队打卡拍照,不过如果不着急,慢慢悠悠逛逛还是不错的,比较推荐,推荐值☆☆
+曾厝垵
一直读不对这个字,都是叫:那个曾什么垵,愧对语文老师,这里到算是意外之喜,鼓浪屿回来已经挺累了,不过由于比较饿(什么原因后面说),并且离住的地方不远,就过去逛了逛,东西还蛮好吃的,芒果挺便宜,一大杯才十块,无骨鸡爪很贵,不是特别爱,臭豆腐不错的,也不算很贵,这里想起来,那边八婆婆的豆乳烧仙草还不错的,去中山路那会喝了,来曾厝垵也买了,奶茶爱好者可以试试,含糖量应该很高,不爱甜食或者减肥的同学慎重考虑好了再尝试,晚上那边从牌坊出来,沿着环岛路挺多夜宵店什么的,非常推荐,推荐值☆☆☆☆
+植物园
植物园还是挺名副其实的,有热带植物,沙漠多肉,因为赶时间逛得不多,热带雨林植物那太多人了,都是在那拍照,而且我指的拍照都是拍人照,本身就很小的路,各种十八线网红,或者普通游客在那摆 pose 拍照,挺无语的;沙漠多肉比较惊喜,好多比人高的仙人掌,一大片的仙人球,很可恶的是好多大仙人掌上都有人刻字,越来越体会到,我们社会人多了,什么样的都有,而且不少;还看了下百花厅,但没什么特别的,可能赶时间比较着急,没仔细看,比较推荐,推荐值☆☆☆
+灵玲马戏团
对这个其实比较排斥,主要是比较晚了,跑的有点远(我太懒了),一开始真的挺拉低体验感受的,上来个什么书法家,现场画马,卖画;不过后面的还算值回票价,主题是花木兰,空中动作应该很考验基本功,然后那些老外的飞轮还跳绳(不知道学名叫啥),动物那块不太忍心看,应该是吃了不少苦头,不过人都这样就往后点再心疼动物吧。
+总结
厦门是个非常适合干饭人的地方,吃饭的地方大部分是差不多一桌菜十个左右就完了,而且上来就一大碗饭,一瓶雪碧一瓶可乐,对于经常是家里跟亲戚吃饭都得十几二十个菜的乡下人来说,不太吃得惯这样的🤦♂️,当然很有可能是我们预算不足,点的差。但是有一点是我回杭州深有感触的,感觉杭州司机的素质真的是跟厦门的司机差了比较多,杭州除非公交车停了,否则人行道很难看到主动让人的,当然这里拿厦门这个旅游城市来对比也不是很公平,不过这也是体现城市现代化文明水平的一个维度吧。
+]]>+ +生活 +旅游 ++ +生活 +杭州 +旅游 +厦门 +中山路 +局口街 +鼓浪屿 +曾厝垵 +植物园 +马戏团 +沙茶面 +海蛎煎 + -聊聊传说中的 ThreadLocal /2021/05/30/%E8%81%8A%E8%81%8A%E4%BC%A0%E8%AF%B4%E4%B8%AD%E7%9A%84-ThreadLocal/ @@ -13666,93 +13763,11 @@ $Java - -Java -ThreadLocal -弱引用 -内存泄漏 -WeakReference -- -聊聊一次 brew update 引发的血案 -/2020/06/13/%E8%81%8A%E8%81%8A%E4%B8%80%E6%AC%A1-brew-update-%E5%BC%95%E5%8F%91%E7%9A%84%E8%A1%80%E6%A1%88/ -熟悉我的人(谁熟悉你啊🙄)知道我以前写过 PHP,虽然现在在工作中没用到了,但是自己的一些小工具还是会用 PHP 来写,但是在 Mac 碰到了一个环境相关的问题,因为我也是个更新狂魔,用了 brew 之后因为 gfw 的原因,如果长时间不更新,有时候要装一个用它装一个软件的话,前置的更新耗时就会让人非常头大,所以我基本会隔天 update 一下,但是这样会带来一个很心烦的问题,就是像这样,因为我是要用一个固定版本的 PHP,如果一直升需要一直配扩展啥的也很麻烦,如果一直升级 PHP 到最新版可能会比较少碰到这个问题 - -
-dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.64.dylib这是什么鬼啊,然后我去这个目录下看了下,已经都是libicui18n.67.dylib了,而且它没有把原来的版本保留下来,首先这个是个叫 icu4c是啥玩意,谷歌了一下
--
-ICU4C是ICU在C/C++平台下的版本, ICU(International Component for Unicode)是基于”IBM公共许可证”的,与开源组织合作研究的, 用于支持软件国际化的开源项目。ICU4C提供了C/C++平台强大的国际化开发能力,软件开发者几乎可以使用ICU4C解决任何国际化的问题,根据各地的风俗和语言习惯,实现对数字、货币、时间、日期、和消息的格式化、解析,对字符串进行大小写转换、整理、搜索和排序等功能,必须一提的是,ICU4C提供了强大的BIDI算法,对阿拉伯语等BIDI语言提供了完善的支持。
-然后首先想到的解决方案就是能不能我使用
-brew install icu4c@64来重装下原来的版本,发现不行,并木有,之前的做法就只能是去网上把 64 的下载下来,然后放到这个目录,比较麻烦不智能,虽然没抱着希望在谷歌着,不过这次竟然给我找到了一个我认为非常 nice 的解决方案,因为是在 Stack Overflow 找到的,本着写给像我这样的小小白看的,那就稍微翻译一下
第一步,我们到 brew的目录下
-cd $(brew --prefix)/Homebrew/Library/Taps/homebrew/homebrew-core/Formula这个可以理解为是 maven 的 pom 文件,不过有很多不同之处,使用ruby 写的,然后一个文件对应一个组件或者软件,那我们看下有个叫icu4c.rb的文件,
-
第二步看看它的提交历史
-git log --follow icu4c.rb在 git log 的海洋中寻找,寻找它的(64版本)的身影
-![]()
第三步注意这三个红框,Stack Overflow 给出来的答案这一步是找到这个 commit id 直接切出一个新分支
-git checkout -b icu4c-63 e7f0f10dc63b1dc1061d475f1a61d01b70ef2cb7其实注意 commit id 旁边的红框,这个是有tag 的,可以直接
-
-git checkout icu4c-64PS: 因为我的问题是出在 64 的问题,Stack Overflow 回答的是 63 的,反正是一样的解决方法
-
第四部,切回去之后我们就可以用 brew 提供的基于文件的安装命令来重新装上 64 版本
-brew reinstall ./icu4c.rb然后就是第五步,切换版本
-
-brew switch icu4c 64.2最后把分支切回来
-
-git checkout master是不是感觉很厉害的解决方法,大佬还提供了一个更牛的,直接写个 zsh 方法
-
-# zsh -function hiicu64() { - local last_dir=$(pwd) - - cd $(brew --prefix)/Homebrew/Library/Taps/homebrew/homebrew-core/Formula - git checkout icu4c-4 - brew reinstall ./icu4c.rb - brew switch icu4c 64.2 - git checkout master - - cd $last_dir -}对应自己的版本改改版本号就可以了,非常好用。
-]]>- -Mac -PHP -Homebrew -icu4c -- -Mac -PHP -Homebrew -icu4c -zsh -- 聊聊厦门旅游的好与不好 -/2021/04/11/%E8%81%8A%E8%81%8A%E5%8E%A6%E9%97%A8%E6%97%85%E6%B8%B8%E7%9A%84%E5%A5%BD%E4%B8%8E%E4%B8%8D%E5%A5%BD/ -这几天去了趟厦门,原来几年前就想去了,本来都请好假了,后面因为一些事情没去成,这次刚好公司组织,就跟 LD 一起去了厦门,也不洋洋洒洒地写游记了,后面可能会有,今天先来总结下好的地方和比较坑的地方。 -
这次主要去了中山路、鼓浪屿、曾厝(cuo)垵、植物园、灵玲马戏团,因为住的离环岛路比较近,还有幸现场看了下厦门马拉松,其中 -中山路
这里看上去是有点民国时期的建筑风格,部分像那种电视里的租界啥的,不过这次去的时候都在翻修,路一大半拦起来了,听导游说这里往里面走有个局口街,然后上次听前同事说厦门比较有名的就是沙茶面和海蛎煎,不出意料的不太爱吃,沙茶面比较普通,可能是没吃到正宗的,海蛎煎吃不惯,倒是有个大叔的沙茶里脊还不错,在局口街那,还有小哥在那拍,应该也算是个网红打卡点了,然后吃了个油条麻糍也还不错,总体如果是看建筑的话可能最近不是个好时间,个人也没这方面爱好,吃的话最好多打听打听沙茶面跟海蛎煎哪里正宗。如果不知道哪家好吃,也不爱看这类建筑的可以排个坑。
-鼓浪屿
鼓浪屿也是完全没啥概念,需要乘船过去,但是只要二十分钟,岛上没有机动车,基本都靠走,有几个比较有名的地方,菽庄花园,里面有钢琴博物馆,对这个感兴趣的可以去看看,旁边是沙滩还可以逛逛,然后有各种博物馆,风琴啥的,岛上最大的特色是巷子多,道听途说有三百多条小巷,还有几个网红打卡点,周杰伦晴天墙,还有个最美转角,都是挤满了人排队打卡拍照,不过如果不着急,慢慢悠悠逛逛还是不错的,比较推荐,推荐值☆☆
-曾厝垵
一直读不对这个字,都是叫:那个曾什么垵,愧对语文老师,这里到算是意外之喜,鼓浪屿回来已经挺累了,不过由于比较饿(什么原因后面说),并且离住的地方不远,就过去逛了逛,东西还蛮好吃的,芒果挺便宜,一大杯才十块,无骨鸡爪很贵,不是特别爱,臭豆腐不错的,也不算很贵,这里想起来,那边八婆婆的豆乳烧仙草还不错的,去中山路那会喝了,来曾厝垵也买了,奶茶爱好者可以试试,含糖量应该很高,不爱甜食或者减肥的同学慎重考虑好了再尝试,晚上那边从牌坊出来,沿着环岛路挺多夜宵店什么的,非常推荐,推荐值☆☆☆☆
-植物园
植物园还是挺名副其实的,有热带植物,沙漠多肉,因为赶时间逛得不多,热带雨林植物那太多人了,都是在那拍照,而且我指的拍照都是拍人照,本身就很小的路,各种十八线网红,或者普通游客在那摆 pose 拍照,挺无语的;沙漠多肉比较惊喜,好多比人高的仙人掌,一大片的仙人球,很可恶的是好多大仙人掌上都有人刻字,越来越体会到,我们社会人多了,什么样的都有,而且不少;还看了下百花厅,但没什么特别的,可能赶时间比较着急,没仔细看,比较推荐,推荐值☆☆☆
-灵玲马戏团
对这个其实比较排斥,主要是比较晚了,跑的有点远(我太懒了),一开始真的挺拉低体验感受的,上来个什么书法家,现场画马,卖画;不过后面的还算值回票价,主题是花木兰,空中动作应该很考验基本功,然后那些老外的飞轮还跳绳(不知道学名叫啥),动物那块不太忍心看,应该是吃了不少苦头,不过人都这样就往后点再心疼动物吧。
-总结
厦门是个非常适合干饭人的地方,吃饭的地方大部分是差不多一桌菜十个左右就完了,而且上来就一大碗饭,一瓶雪碧一瓶可乐,对于经常是家里跟亲戚吃饭都得十几二十个菜的乡下人来说,不太吃得惯这样的🤦♂️,当然很有可能是我们预算不足,点的差。但是有一点是我回杭州深有感触的,感觉杭州司机的素质真的是跟厦门的司机差了比较多,杭州除非公交车停了,否则人行道很难看到主动让人的,当然这里拿厦门这个旅游城市来对比也不是很公平,不过这也是体现城市现代化文明水平的一个维度吧。
-]]>- -生活 -旅游 -- 生活 -杭州 -旅游 -厦门 -中山路 -局口街 -鼓浪屿 -曾厝垵 -植物园 -马戏团 -沙茶面 -海蛎煎 +Java +ThreadLocal +弱引用 +内存泄漏 +WeakReference @@ -13772,6 +13787,30 @@ function hiicu64() { +电瓶车 + 聊聊我刚学会的应用诊断方法 +/2020/05/22/%E8%81%8A%E8%81%8A%E6%88%91%E5%88%9A%E5%AD%A6%E4%BC%9A%E7%9A%84%E5%BA%94%E7%94%A8%E8%AF%8A%E6%96%AD%E6%96%B9%E6%B3%95/ +因为传说中的出身问题,我以前写的是PHP,在使用 swoole 之前,基本的应用调试手段就是简单粗暴的 var_dump,exit,对于单进程模型的 PHP 也是简单有效,技术栈换成 Java 之后,就变得没那么容易,一方面是需要编译,另一方面是一般都是基于 spring 的项目,如果问题定位比较模糊,那框架层的是很难靠简单的 System.out.println 或者打 log 解决,(PS:我觉得可能我写的东西比较适合从 PHP 这种弱类型语言转到 Java 的小白同学)这个时候一方面因为是 Java,有了非常好用的 idea IDE 的支持,可以各种花式调试,条件断点尤其牛叉,但是又因为有 Spring+Java 的双重原因,有些情况下单步调试可以把手按废掉,这也是我之前一直比较困惑苦逼的点,后来随着慢慢精(jiang)进(you)之后,比如对于一个 oom 的情况,我们可以通过启动参数加上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=xx/xx 来配置溢出时的堆dump 日志,获取到这个文件后,我们可以通过像 Memory Analyzer (MAT)[https://www.eclipse.org/mat/] (The Eclipse Memory Analyzer is a fast and feature-rich Java heap analyzer that helps you find memory leaks and reduce memory consumption.)来查看诊断问题所在,之前用到的时候是因为有个死循环一直往链表里塞数据,属于比较简单的,后来一次是由于运维进行应用迁移时按默认的统一配置了堆内存大小,导致内存的确不够用,所以溢出了, +
今天想说的其实主要是我们的 thread dump,这也是我最近才真正用的一个方法,可能真的很小白了,用过 ide 的单步调试其实都知道会有一个一层层的玩意,比如函数从 A,调用了 B,再从 B 调用了 C,一直往下(因为是 Java,所以还有很多🤦♂️),这个其实也是大部分语言的调用模型,利用了栈这个数据结构,通过这个结构我们可以知道代码的调用链路,由于对于一个 spring 应用,在本身框架代码量非常庞大的情况下,外加如果应用代码也是非常多的时候,有时候通过单步调试真的很难短时间定位到问题,需要非常大的耐心和仔细观察,当然不是说完全不行,举个例子当我的应用经常启动需要非常长的时间,因为本身应用有非常多个 bean,比较难说究竟是 bean 的加载的确很慢还是有什么异常原因,这种时候就可以使用 thread dump 了,具体怎么操作呢![]()
如果在idea 中运行或者调试时,可以直接点击这个照相机一样的按钮,右边就会出现了左边会显示所有的线程,右边会显示线程栈, +
+"main@1" prio=5 tid=0x1 nid=NA runnable + java.lang.Thread.State: RUNNABLE + at TreeDistance.treeDist(TreeDistance.java:64) + at TreeDistance.treeDist(TreeDistance.java:65) + at TreeDistance.treeDist(TreeDistance.java:65) + at TreeDistance.treeDist(TreeDistance.java:65) + at TreeDistance.main(TreeDistance.java:45)这就是我们主线程的堆栈信息了,main 表示这个线程名,prio表示优先级,默认是 5,tid 表示线程 id,nid 表示对应的系统线程,后面的runnable 表示目前线程状态,因为是被我打了断点,所以是就许状态,然后下面就是对应的线程栈内容了,在
+]]>TreeDistance类的treeDist方法中,对应的文件行数是 64 行。
这里使用 thread dump一般也不会是上面我截图代码里的这种代码量很少的,一般是大型项目,有时候跑着跑着没反应,又不知道跑到哪了,特别是一些刚接触的大项目或者需要定位一个大项目的一个疑难问题,一时没思路时,可以使用这个方法,个人觉得非常有帮助。+ +Java +Thread dump +问题排查 +工具 ++ +Java +Thread dump + -聊聊我理解的分布式事务 /2020/05/17/%E8%81%8A%E8%81%8A%E6%88%91%E7%90%86%E8%A7%A3%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/ @@ -13816,30 +13855,6 @@ function hiicu64() {3PC - 聊聊我刚学会的应用诊断方法 -/2020/05/22/%E8%81%8A%E8%81%8A%E6%88%91%E5%88%9A%E5%AD%A6%E4%BC%9A%E7%9A%84%E5%BA%94%E7%94%A8%E8%AF%8A%E6%96%AD%E6%96%B9%E6%B3%95/ -因为传说中的出身问题,我以前写的是PHP,在使用 swoole 之前,基本的应用调试手段就是简单粗暴的 var_dump,exit,对于单进程模型的 PHP 也是简单有效,技术栈换成 Java 之后,就变得没那么容易,一方面是需要编译,另一方面是一般都是基于 spring 的项目,如果问题定位比较模糊,那框架层的是很难靠简单的 System.out.println 或者打 log 解决,(PS:我觉得可能我写的东西比较适合从 PHP 这种弱类型语言转到 Java 的小白同学)这个时候一方面因为是 Java,有了非常好用的 idea IDE 的支持,可以各种花式调试,条件断点尤其牛叉,但是又因为有 Spring+Java 的双重原因,有些情况下单步调试可以把手按废掉,这也是我之前一直比较困惑苦逼的点,后来随着慢慢精(jiang)进(you)之后,比如对于一个 oom 的情况,我们可以通过启动参数加上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=xx/xx 来配置溢出时的堆dump 日志,获取到这个文件后,我们可以通过像 Memory Analyzer (MAT)[https://www.eclipse.org/mat/] (The Eclipse Memory Analyzer is a fast and feature-rich Java heap analyzer that helps you find memory leaks and reduce memory consumption.)来查看诊断问题所在,之前用到的时候是因为有个死循环一直往链表里塞数据,属于比较简单的,后来一次是由于运维进行应用迁移时按默认的统一配置了堆内存大小,导致内存的确不够用,所以溢出了, -
今天想说的其实主要是我们的 thread dump,这也是我最近才真正用的一个方法,可能真的很小白了,用过 ide 的单步调试其实都知道会有一个一层层的玩意,比如函数从 A,调用了 B,再从 B 调用了 C,一直往下(因为是 Java,所以还有很多🤦♂️),这个其实也是大部分语言的调用模型,利用了栈这个数据结构,通过这个结构我们可以知道代码的调用链路,由于对于一个 spring 应用,在本身框架代码量非常庞大的情况下,外加如果应用代码也是非常多的时候,有时候通过单步调试真的很难短时间定位到问题,需要非常大的耐心和仔细观察,当然不是说完全不行,举个例子当我的应用经常启动需要非常长的时间,因为本身应用有非常多个 bean,比较难说究竟是 bean 的加载的确很慢还是有什么异常原因,这种时候就可以使用 thread dump 了,具体怎么操作呢![]()
如果在idea 中运行或者调试时,可以直接点击这个照相机一样的按钮,右边就会出现了左边会显示所有的线程,右边会显示线程栈, -
-"main@1" prio=5 tid=0x1 nid=NA runnable - java.lang.Thread.State: RUNNABLE - at TreeDistance.treeDist(TreeDistance.java:64) - at TreeDistance.treeDist(TreeDistance.java:65) - at TreeDistance.treeDist(TreeDistance.java:65) - at TreeDistance.treeDist(TreeDistance.java:65) - at TreeDistance.main(TreeDistance.java:45)这就是我们主线程的堆栈信息了,main 表示这个线程名,prio表示优先级,默认是 5,tid 表示线程 id,nid 表示对应的系统线程,后面的runnable 表示目前线程状态,因为是被我打了断点,所以是就许状态,然后下面就是对应的线程栈内容了,在
-]]>TreeDistance类的treeDist方法中,对应的文件行数是 64 行。
这里使用 thread dump一般也不会是上面我截图代码里的这种代码量很少的,一般是大型项目,有时候跑着跑着没反应,又不知道跑到哪了,特别是一些刚接触的大项目或者需要定位一个大项目的一个疑难问题,一时没思路时,可以使用这个方法,个人觉得非常有帮助。- -Java -Thread dump -问题排查 -工具 -- -Java -Thread dump -聊聊最近平淡的生活之又聊通勤 /2021/11/07/%E8%81%8A%E8%81%8A%E6%9C%80%E8%BF%91%E5%B9%B3%E6%B7%A1%E7%9A%84%E7%94%9F%E6%B4%BB/ @@ -13860,99 +13875,23 @@ function hiicu64() {- 聊聊 Sharding-Jdbc 分库分表下的分页方案 -/2022/01/09/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8%E4%B8%8B%E7%9A%84%E5%88%86%E9%A1%B5%E6%96%B9%E6%A1%88/ -前面在聊 Sharding-Jdbc 的时候看到了一篇文章,关于一个分页的查询,一直比较直接的想法就是分库分表下的分页是非常不合理的,一般我们的实操方案都是分表加上 ES 搜索做分页,或者通过合表读写分离的方案,因为对于 sharding-jdbc 如果没有带分表键,查询基本都是只能在所有分表都执行一遍,然后再加上分页,基本上是分页越大后续的查询越耗资源,但是仔细的去想这个细节还是这次,就简单说说
首先就是我的分表结构 -
-CREATE TABLE `student_time_0` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `name` varchar(200) COLLATE utf8_bin DEFAULT NULL, - `age` tinyint(3) unsigned DEFAULT NULL, - `create_time` bigint(20) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=674 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;有这样的三个表,
-student_time_0,student_time_1,student_time_2, 以 user_id 作为分表键,根据表数量取模作为分表依据
这里先构造点数据,
-insert into student_time (`name`, `user_id`, `age`, `create_time`) values (?, ?, ?, ?)主要是为了保证
-create_time唯一比较好说明问题,
-int i = 0; -try ( - Connection conn = dataSource.getConnection(); - PreparedStatement ps = conn.prepareStatement(insertSql)) { - do { - ps.setString(1, localName + new Random().nextInt(100)); - ps.setLong(2, 10086L + (new Random().nextInt(100))); - ps.setInt(3, 18); - ps.setLong(4, new Date().getTime()); - - - int result = ps.executeUpdate(); - LOGGER.info("current execute result: {}", result); - Thread.sleep(new Random().nextInt(100)); - i++; - } while (i <= 2000);三个表的数据分别是 673,678,650,说明符合预期了,各个表数据不一样,接下来比如我们想要做一个这样的分页查询
-
-select * from student_time ORDER BY create_time ASC limit 1000, 5;
-student_time对于我们使用的sharding-jdbc来说当然是逻辑表,首先从一无所知去想这个查询如果我们自己来处理应该是怎么做,
首先是不是可以每个表都从 333 开始取 5 条数据,类似于下面的查询,然后进行 15 条的合并重排序获取前面的 5 条
-select * from student_time_0 ORDER BY create_time ASC limit 333, 5; -select * from student_time_1 ORDER BY create_time ASC limit 333, 5; -select * from student_time_2 ORDER BY create_time ASC limit 333, 5;忽略前面 limit 差的 1,这个结果除非三个表的分布是绝对的均匀,否则结果肯定会出现一定的偏差,以为每个表的 333 这个位置对于其他表来说都不一定是一样的,这样对于最后整体的结果,就会出现偏差
-
因为一直在纠结怎么让这个更直观的表现出来,所以尝试画了个图![]()
黑色的框代表我从每个表里按排序从 334 到 338 的 5 条数据, 他们在每个表里都是代表了各自正确的排序值,但是对于我们想要的其实是合表后的 1001,1005 这五条,然后我们假设总的排序值位于前 1000 的分布是第 0 个表是 320 条,第 1 个表是 340 条,第 2 个表是 340 条,那么可以明显地看出来我这么查的结果简单合并肯定是不对的。
那么 sharding-jdbc 是如何保证这个结果的呢,其实就是我在每个表里都查分页偏移量和分页大小那么多的数据,在我这个例子里就是对于 0,1,2 三个分表每个都查 1005 条数据,即使我的数据不平衡到最极端的情况,前 1005 条数据都出在某个分表中,也可以正确获得最后的结果,但是明显的问题就是大分页,数据较多,就会导致非常大的问题,即使如 sharding-jdbc 对于合并排序的优化做得比较好,也还是需要传输那么大量的数据,并且查询也耗时,那么有没有解决方案呢,应该说有两个,或者说主要是想讲后者
第一个办法是像这种查询,如果业务上不需要进行跳页,而是只给下一页,那么我们就能把前一次的最大偏移量的 create_time 记录下来,下一页就可以拿着这个偏移量进行查询,这个比较简单易懂,就不多说了
第二个办法是看的58 沈剑的一篇文章,尝试理解讲述一下,
这个办法的第一步跟前面那个错误的方法或者说不准确的方法一样,先是将分页偏移量平均后在三个表里进行查询
-t0 -334 10158 nick95 18 1641548941767 -335 10098 nick11 18 1641548941879 -336 10167 nick51 18 1641548942089 -337 10167 nick3 18 1641548942119 -338 10170 nick57 18 1641548942169 - - -t1 -334 10105 nick98 18 1641548939071 最小 -335 10174 nick94 18 1641548939377 -336 10129 nick85 18 1641548939442 -337 10141 nick84 18 1641548939480 -338 10096 nick74 18 1641548939668 - -t2 -334 10184 nick11 18 1641548945075 -335 10109 nick93 18 1641548945382 -336 10181 nick41 18 1641548945583 -337 10130 nick80 18 1641548945993 -338 10184 nick19 18 1641548946294 最大然后要做什么呢,其实目标比较明白,因为前面那种方法其实就是我知道了前一页的偏移量,所以可以直接当做条件来进行查询,那这里我也想着拿到这个条件,所以我将第一遍查出来的最小的 create_time 和最大的 create_time 找出来,然后再去三个表里查询,其实主要是最小值,因为我拿着最小值去查以后我就能知道这个最小值在每个表里处在什么位置,
-
-t0 -322 10161 nick81 18 1641548939284 -323 10113 nick16 18 1641548939393 -324 10110 nick56 18 1641548939577 -325 10116 nick69 18 1641548939588 -326 10173 nick51 18 1641548939646 - -t1 -334 10105 nick98 18 1641548939071 -335 10174 nick94 18 1641548939377 -336 10129 nick85 18 1641548939442 -337 10141 nick84 18 1641548939480 -338 10096 nick74 18 1641548939668 - -t2 -297 10136 nick28 18 1641548939161 -298 10142 nick68 18 1641548939177 -299 10124 nick41 18 1641548939237 -300 10148 nick87 18 1641548939510 -301 10169 nick23 18 1641548939715我只贴了前五条数据,为了方便知道偏移量,每个分表都使用了自增主键,我们可以看到前一次查询的最小值分别在其他两个表里的位置分别是 322-1 和 297-1,那么对于总体来说这个时间应该是在
+322 - 1 + 333 + 297 - 1 = 951,那这样子我只要对后面的数据最多每个表查1000 - 951 + 5 = 54条数据再进行合并排序就可以获得最终正确的结果。
这个就是传说中的二次查询法。聊聊最近平淡的生活之看《神探狄仁杰》 +/2021/12/19/%E8%81%8A%E8%81%8A%E6%9C%80%E8%BF%91%E5%B9%B3%E6%B7%A1%E7%9A%84%E7%94%9F%E6%B4%BB%E4%B9%8B%E7%9C%8B%E3%80%8A%E7%A5%9E%E6%8E%A2%E7%8B%84%E4%BB%81%E6%9D%B0%E3%80%8B/ +其实最近看的不止这一部,前面看了《继承者们》,《少年包青天》这些,就一起聊下,其中看《继承者们》算是个人比较喜欢,以前就有这种看剧的习惯,这个跟《一生一世》里任嘉伦说自己看《寻秦记》看了几十遍一样,我看喜欢的剧也基本上会看不止五遍,继承者们是有帅哥美女看,而且印象中剧情也挺甜的,一般情况下最好是已经有点遗忘剧情了,因为我个人觉得看剧分两种,无聊了又心情不太好,可以看些这类轻松又看过的剧,可以不完全专心地看剧,另外有心情专心看的时候,可以看一些需要思考,一些探案类的或者烧脑类。
最近看了《神探狄仁杰》,因为跟前面看的《少年包青天》都是这类古装探案剧,正好有些感想,《少年包青天》算是儿时阴影,本来是不太会去看的,正好有一次有机会跟 LD 一起看了会就也觉得比较有意思就看了下去,不得不说,以前的这些剧还是很不错的,包括剧情和演员,第一部一共是 40 集,看的过程中也发现了大概是五个案子,平均八集一个案子,整体节奏还是比较慢的,但是基本每个案子其实都是构思得很巧妙,很久以前看过但是现在基本不太记得剧情了,每个案子在前面几集的时候基本都猜不到犯案逻辑,但是在看了狄仁杰之后,发现两部剧也有比较大的差别,少年包青天相对来说逻辑性会更强一些,个人主观觉得推理的严谨性更高,可能剧本打磨上更将就一下,而狄仁杰因为要提现他的个人强项,不比少年包青天中有公孙策一时瑜亮的情节,狄仁杰中分工明确,李元芳是个武力担当,曾泰是捧哏的,相对来说是狄仁杰在案子里从始至终地推进案情,有些甚至有些玄乎,会有一些跳脱跟不合理,有些像是狄仁杰的奇遇,不过这些想法是私人的观点,并不是想要评孰优孰劣;第二个感受是不知道是不是年代关系,特别是少年包青天,每个案件的大 boss 基本都不是个完全的坏人,甚至都是比较情有可原的可怜人,因为一些特殊原因,而好几个都是包拯身边的人,这一点其实跟狄仁杰里第一个使团惊魂案件比较类似,虎敬晖也是个人物形象比较丰满的角色,不是个标签化的淡薄形象,跟金木兰的感情和反叛行动在最后都说明的缘由,而且也有随着跟狄仁杰一起办案被其影响感化,最终为了救狄仁杰而被金木兰所杀,只是这样金木兰这个角色就会有些偏执和符号化,当然剧本肯定不是能面面俱到,这样的剧本已经比现在很多流量剧的好很多了。还想到了前阵子看的《指环王》中的白袍萨鲁曼在剧中也是个比较单薄的角色,这样的电影彪炳影史也没办法把个个人物都设计得完整有血有肉,或者说这本来也是应该有侧重点,当然其实我也不觉得指环王就是绝对的最好的,因为相对来说故事情节的复杂性等真的不如西游记,只是在 86 版之后的各种乱七八糟的翻牌和乱拍已经让这个真正的王者神话故事有点力不从心,这里边有部西游记后传是个人还比较喜欢的,虽然武打动作比较鬼畜,但是剧情基本是无敌的,在西游的架构上衍生出来这么完整丰富的故事,人物角色也都有各自的出彩点。
说回狄仁杰,在这之前也看过徐克拍的几部狄仁杰的电影版,第一部刘德华拍得相对完成度更高,故事性也可圈可点,后面几部就是剧情拉胯,靠特效拉回来一点分,虽说这个也是所谓的狄仁杰宇宙的构思在里面但是现在来看基本是跟西游那些差不多,完全没有整体性可言,打一枪换一个地方,演员也没有延续性,剧情也是前后跳脱,没什么关联跟承上启下,导致质量层次不一,更不用谈什么狄仁杰宇宙了,不过这个事情也是难说,原因很多,现在资本都是更加趋利的,一些需要更长久时间才能有回报的投资是很难获得资本青睐,所以只能将重心投给选择一些流量明星,而本来应该将资源投给剧本打磨的基本就没了,再深入说也没意义了,社会现状就是这样。
还有一点感想是,以前的剧里的拍摄环境还是比较惨的,看着一些房子,甚至皇宫都是比较破旧的,地面还是石板这种,想想以前的演员的环境再想想现在的,比如成龙说的,以前他拍剧就是啪摔了,问这条有没有过,过了就直接送医院,而不是现在可能手蹭破点皮就大叫,甚至还有饭圈这些破事。 ]]>- Java +生活 - Java -Sharding-Jdbc +生活 +看剧 - +聊聊最近平淡的生活之看《神探狄仁杰》 -/2021/12/19/%E8%81%8A%E8%81%8A%E6%9C%80%E8%BF%91%E5%B9%B3%E6%B7%A1%E7%9A%84%E7%94%9F%E6%B4%BB%E4%B9%8B%E7%9C%8B%E3%80%8A%E7%A5%9E%E6%8E%A2%E7%8B%84%E4%BB%81%E6%9D%B0%E3%80%8B/ -其实最近看的不止这一部,前面看了《继承者们》,《少年包青天》这些,就一起聊下,其中看《继承者们》算是个人比较喜欢,以前就有这种看剧的习惯,这个跟《一生一世》里任嘉伦说自己看《寻秦记》看了几十遍一样,我看喜欢的剧也基本上会看不止五遍,继承者们是有帅哥美女看,而且印象中剧情也挺甜的,一般情况下最好是已经有点遗忘剧情了,因为我个人觉得看剧分两种,无聊了又心情不太好,可以看些这类轻松又看过的剧,可以不完全专心地看剧,另外有心情专心看的时候,可以看一些需要思考,一些探案类的或者烧脑类。
最近看了《神探狄仁杰》,因为跟前面看的《少年包青天》都是这类古装探案剧,正好有些感想,《少年包青天》算是儿时阴影,本来是不太会去看的,正好有一次有机会跟 LD 一起看了会就也觉得比较有意思就看了下去,不得不说,以前的这些剧还是很不错的,包括剧情和演员,第一部一共是 40 集,看的过程中也发现了大概是五个案子,平均八集一个案子,整体节奏还是比较慢的,但是基本每个案子其实都是构思得很巧妙,很久以前看过但是现在基本不太记得剧情了,每个案子在前面几集的时候基本都猜不到犯案逻辑,但是在看了狄仁杰之后,发现两部剧也有比较大的差别,少年包青天相对来说逻辑性会更强一些,个人主观觉得推理的严谨性更高,可能剧本打磨上更将就一下,而狄仁杰因为要提现他的个人强项,不比少年包青天中有公孙策一时瑜亮的情节,狄仁杰中分工明确,李元芳是个武力担当,曾泰是捧哏的,相对来说是狄仁杰在案子里从始至终地推进案情,有些甚至有些玄乎,会有一些跳脱跟不合理,有些像是狄仁杰的奇遇,不过这些想法是私人的观点,并不是想要评孰优孰劣;第二个感受是不知道是不是年代关系,特别是少年包青天,每个案件的大 boss 基本都不是个完全的坏人,甚至都是比较情有可原的可怜人,因为一些特殊原因,而好几个都是包拯身边的人,这一点其实跟狄仁杰里第一个使团惊魂案件比较类似,虎敬晖也是个人物形象比较丰满的角色,不是个标签化的淡薄形象,跟金木兰的感情和反叛行动在最后都说明的缘由,而且也有随着跟狄仁杰一起办案被其影响感化,最终为了救狄仁杰而被金木兰所杀,只是这样金木兰这个角色就会有些偏执和符号化,当然剧本肯定不是能面面俱到,这样的剧本已经比现在很多流量剧的好很多了。还想到了前阵子看的《指环王》中的白袍萨鲁曼在剧中也是个比较单薄的角色,这样的电影彪炳影史也没办法把个个人物都设计得完整有血有肉,或者说这本来也是应该有侧重点,当然其实我也不觉得指环王就是绝对的最好的,因为相对来说故事情节的复杂性等真的不如西游记,只是在 86 版之后的各种乱七八糟的翻牌和乱拍已经让这个真正的王者神话故事有点力不从心,这里边有部西游记后传是个人还比较喜欢的,虽然武打动作比较鬼畜,但是剧情基本是无敌的,在西游的架构上衍生出来这么完整丰富的故事,人物角色也都有各自的出彩点。
说回狄仁杰,在这之前也看过徐克拍的几部狄仁杰的电影版,第一部刘德华拍得相对完成度更高,故事性也可圈可点,后面几部就是剧情拉胯,靠特效拉回来一点分,虽说这个也是所谓的狄仁杰宇宙的构思在里面但是现在来看基本是跟西游那些差不多,完全没有整体性可言,打一枪换一个地方,演员也没有延续性,剧情也是前后跳脱,没什么关联跟承上启下,导致质量层次不一,更不用谈什么狄仁杰宇宙了,不过这个事情也是难说,原因很多,现在资本都是更加趋利的,一些需要更长久时间才能有回报的投资是很难获得资本青睐,所以只能将重心投给选择一些流量明星,而本来应该将资源投给剧本打磨的基本就没了,再深入说也没意义了,社会现状就是这样。
还有一点感想是,以前的剧里的拍摄环境还是比较惨的,看着一些房子,甚至皇宫都是比较破旧的,地面还是石板这种,想想以前的演员的环境再想想现在的,比如成龙说的,以前他拍剧就是啪摔了,问这条有没有过,过了就直接送医院,而不是现在可能手蹭破点皮就大叫,甚至还有饭圈这些破事。 +聊聊最近平淡的生活之《花束般的恋爱》观后感 +/2021/12/31/%E8%81%8A%E8%81%8A%E6%9C%80%E8%BF%91%E5%B9%B3%E6%B7%A1%E7%9A%84%E7%94%9F%E6%B4%BB%E4%B9%8B%E3%80%8A%E8%8A%B1%E6%9D%9F%E8%88%AC%E7%9A%84%E6%81%8B%E7%88%B1%E3%80%8B%E8%A7%82%E5%90%8E%E6%84%9F/ +周末在领导的提议下看了豆瓣的年度榜单,本来感觉没啥心情看的,看到主演有有村架纯就觉得可以看一下,颜值即正义嘛,男主小麦跟女主小娟(后面简称小麦跟小娟)是两个在一次非常偶然的没赶上地铁末班车事件中相识,这里得说下日本这种通宵营业的店好像挺不错的,看着也挺正常,国内估计只有酒吧之类的可以。晚上去的地方是有点暗暗的,好像也有点类似酒吧,旁边有类似于 dj 那种,然后同桌的还有除了男女主的另外一对男女,也是因为没赶上地铁末班车的,但也是陌生人,然后小麦突然看到了有个非常有名的电影人,小娟竟然也认识,然后旁边那对完全不认识,还在那吹自己看过很多电影,比如《肖申克的救赎》,于是男女主都特别鄙夷地看着他们,然后他们又去了另一个有点像泡澡的地方席地而坐,他们发现了自己的鞋子都是一样的,然后在女的去上厕所的时候,小麦暗恋的学姐也来了,然后小麦就去跟学姐他们一起坐了,小娟回来后有点不开心就说去朋友家睡,幸好小麦看出来了(他竟然看出来了,本来以为应该是没填过恋爱很木讷的),就追出去,然后就去了小麦家,到了家小娟发现小麦家的书柜上的书简直就跟她自己家的一模一样,小麦还给小娟吹了头发,一起吃烤饭团,看电影,第二天送小娟上了公交,还约好了一起看木乃伊展,然而并没有交换联系方式,但是他们还是约上了一起看了木乃伊展,在餐馆就出现了片头那一幕的来源,因为餐馆他们想一起听歌,就用有线耳机一人一个耳朵听,但是旁边就有个大叔说“你们是不是不爱音乐,左右耳朵是不一样的,只有一起听才是真正的音乐”这样的话,然后的剧情有点跳,因为是指他们一直在这家餐馆吃饭,中间有他们一起出去玩情节穿插着,也是在这他们确立了关系,可以说主体就是体现了他们非常的合拍和默契,就像一些影评说的,这部电影是说如何跟百分百合拍的人分手,然后就是正常的恋爱开始啪啪啪,一直腻在床上,也没去就业说明会,后面也有讲了一点小麦带着小娟去认识他的朋友,也把小娟介绍给了他们认识,这里算是个小伏笔,后面他们分手也有这里的人的一些关系,接下去的剧情说实话我是不太喜欢的,如果一部八分的电影只是说恋爱被现实打败的话,我觉得在我这是不合格的,但是事实也是这样,小麦其实是有家里的资助,所以后面还是按自己的喜好给一些机构画点插画,小娟则要出去工作,因为小娟家庭观念也是要让她出去有正经工作,用脚指头想也能知道肯定不顺利,然后就是暂时在一家蛋糕店工作,小麦就每天去接小娟,日子过得甜甜蜜蜜,后面小娟在自己的努力下考了个什么资格证,去了一家医院还是什么做前台行政,这中间当然就有父母来见面吃饭了,他们在开始恋爱不久就同居合租了,然后小娟父母就是来说要让她有个正经工作,对男的说的话就是人生就是责任这类的话,而小麦爸爸算是个导火索,因为小麦家里是做烟花生意的,他爸让他就做烟花生意,因为要回老家,并且小麦也不想做,所以就拒绝了,然后他爸就说不给他每个月五万的资助,这也导致了小麦需要去找工作,这个过程也是很辛苦,本来想要年前找好工作,然后事与愿违,后面有一次小娟被同事吐槽怎么从来不去团建,于是她就去了(我以为会拒绝),正在团建的时候小麦给她电话,说找到工作了,是一个创业物流公司这种,这里剧情就是我觉得比较俗套的,小麦各种被虐,累成狗,但是就像小娟爸爸说的话,人生就是责任,所以一直在坚持,但是这样也导致了跟小娟的交流也越来越少,他们原来最爱的漫画,爱玩的游戏,也只剩小娟一个人看,一个人玩,而正是这个时候,小娟说她辞掉了工作,去做一个不是太靠谱的漫画改造的密室逃脱,然后这里其实有一点后面争议很大的,就是这个工作其实是前面小麦介绍给小娟的那些朋友中一个的女朋友介绍的,而在有个剧情就是小娟有一次在这个密室逃脱的老板怀里醒过来,是在 KTV 那样的场景里,这就有很多人觉得小娟是不是出轨了,我觉得其实不那么重要,因为这个离职的事情已经让一切矛盾都摆在眼前,小麦其实是接受这种需要承担责任的生活,也想着要跟小娟结婚,但是小娟似乎还是想要过着那样理想的生活,做自己想做的事情,看自己爱看的漫画,也要小麦能像以前那样一直那么默契的有着相同的爱好,这里的触发点其实还有个是那个小麦的朋友(也就是他女朋友介绍小娟那个不靠谱工作的)的葬礼上,小麦在参加完葬礼后有挺多想倾诉的,而小娟只是想睡了,这个让小麦第二天起来都不想理小娟,只是这里我不太理解,难道这点闹情绪都不能接受吗,所谓的合拍也只是毫无限制的情况下的合拍吧,真正的生活怎么可能如此理想呢,即使没有物质生活的压力,也会有其他的各种压力和限制,在这之后其实小麦想说的是小娟是不是没有想跟自己继续在一起的想法了,而小娟觉得都不说话了,还怎么结婚呢,后面其实导演搞了个小 trick,突然放了异常婚礼,但是不是男女主的,我并不觉得这个桥段很好,在婚礼里男女主都觉得自己想要跟对方说分手了,但是当他们去了最开始一直去的餐馆的时候,一个算是一个现实映照的就是他们一直坐的位子被占了,可能也是导演想通过这个来说明他们已经回不去了,在餐馆交谈的时候,小麦其实是说他们结婚吧,并没有想前面婚礼上预设地要分手,但是小娟放弃了,不想结婚,因为不想过那样的生活了,而小麦觉得可能生活就是那样,不可能一直保持刚恋爱时候的那种感觉,生活就是责任,人生就意味着责任。 + 我的一些观点也在前面说了,恋爱到婚姻,即使物质没问题,经济没问题,也会有各种各样的问题,需要一起去解决,因为结婚就意味着需要相互扶持,而不是各取所需,可能我的要求比较高,后面男女主在分手后还一起住了一段时间,我原来还在想会不会通过这个方式让他们继续去磨合同步,只是我失望了,最后给个打分可能是 5 到 6 分吧,勉强及格,好的影视剧应该源于生活高于生活,这一部可能还比不上生活。
]]>生活 @@ -13998,6 +13937,22 @@ t2修电脑的 + 聊聊这次换车牌及其他 +/2022/02/20/%E8%81%8A%E8%81%8A%E8%BF%99%E6%AC%A1%E6%8D%A2%E8%BD%A6%E7%89%8C%E5%8F%8A%E5%85%B6%E4%BB%96/ +去年 8 月份运气比较好,摇到了车牌,本来其实应该很早就开始摇的,前面第一次换工作没注意社保断缴了一个月,也是大意失荆州,后面到了 17 年社保满两年了,好像只摇了一次,还是就没摇过,有点忘了,好像是什么原因导致那次也没摇成功,但是后面暂住证就取消了,需要居住证,居住证又要一年及以上的租房合同,并且那会买车以后也不怎么开,住的地方车位还好,但是公司车位一个月要两三千,甚至还是打车上下班比较实惠,所以也没放在心上,后面摇到房以后,也觉得应该准备起来车子,就开始办了居住证,居住证其实还可以用劳动合同,而且办起来也挺快,大概是三四月份开始摇,到 8 月份的某一天收到短信说摇到了,一开始还挺开心,不过心里抱着也不怎么开,也没怎么大放在心上,不过这里有一点就是我把那个照片直接发出去,上面有着我的身份证号,被 LD 说了一顿,以后也应该小心点,但是后面不知道是哪里看了下,说杭州上牌已经需要国六标准的车了,瞬间感觉是空欢喜了,可是有同事说是可以的,我就又打了官方的电话,结果说可以的,要先转籍,然后再做上牌。 + +转籍其实是很方便的,在交警 12123 App 上申请就行了,在转籍以后,需要去实地验车,验车的话,在支付宝-杭州交警生活号里进行预约,找就近的车管所就好,需要准备一些东西,首先是行驶证,机动车登记证书,身份证,居住证,还有车上需要准备的东西是要有三脚架和反光背心,反光背心是最近几个月开始要的,问过之前去验车的只需要三脚架就好了,预约好了的话建议是赶上班时间越早越好,不然过去排队时间要很久,而且人多了以后会很乱,各种插队,而且有很多都是汽车销售,一个销售带着一堆车,我们附近那个进去的小路没一会就堵满车,进去需要先排队,然后扫码,接着交资料,这两个都排着队,如果去晚了就要排很久的队,交完资料才是排队等验车,验车就是打开引擎盖,有人会帮忙拓印发动机车架号,然后验车的会各种检查一下,车里面,还有后备箱,建议车内整理干净点,后备箱不要放杂物,检验完了之后,需要把三脚架跟反光背心放在后备箱盖子上,人在旁边拍个照,然后需要把车牌遮住后再拍个车子的照片,再之后就是去把车牌卸了,这个多吐槽下,那边应该是本来那边师傅帮忙卸车牌,结果他就说是教我们拆,虽然也不算难,但是不排除师傅有在偷懒,完了之后就是把旧车牌交回去,然后需要在手机上(警察叔叔 App)提交各种资料,包括身份证,行驶证,机动车登记证书,提交了之后就等寄车牌过来了。
+这里面缺失的一个环节就是选号了,选号杭州有两个方式,一种就是根据交管局定期发布的选号号段,可以自定义拼 20 个号,在手机上的交警 12123 App 上可以三个一组的形式提交,如果有没被选走的,就可以预选到这个了,但是这种就是也需要有一定策略,最新出的号段能选中的概率大一点,然后数字全是 8,6 这种的肯定会一早就被选走,然后如果跟我一样可以提前选下尾号,因为尾号数字影响限号,我比较有可能周五回家,所以得避开 5,0 的,第二种就是 50 选一跟以前新车选号一样,就不介绍了。第一种选中了以后可以在前面交还旧车牌的时候填上等着寄过来了,因为我是第一种选中的,第二种也可以在手机上选,也在可以在交还车牌的时候现场选。
+总体过程其实是 LD 在各种查资料跟帮我跑来跑去,要不是 LD,估计在交管局那边我就懵逼了,各种插队,而且车子开着车子,也不能随便跑,所以建议办这个的时候有个人一起比较好。
+]]>+ +生活 ++ +生活 +换车牌 +聊聊那些加塞狗 /2021/01/17/%E8%81%8A%E8%81%8A%E9%82%A3%E4%BA%9B%E5%8A%A0%E5%A1%9E%E7%8B%97/ @@ -14087,33 +14042,78 @@ t2- -聊聊最近平淡的生活之《花束般的恋爱》观后感 -/2021/12/31/%E8%81%8A%E8%81%8A%E6%9C%80%E8%BF%91%E5%B9%B3%E6%B7%A1%E7%9A%84%E7%94%9F%E6%B4%BB%E4%B9%8B%E3%80%8A%E8%8A%B1%E6%9D%9F%E8%88%AC%E7%9A%84%E6%81%8B%E7%88%B1%E3%80%8B%E8%A7%82%E5%90%8E%E6%84%9F/ -周末在领导的提议下看了豆瓣的年度榜单,本来感觉没啥心情看的,看到主演有有村架纯就觉得可以看一下,颜值即正义嘛,男主小麦跟女主小娟(后面简称小麦跟小娟)是两个在一次非常偶然的没赶上地铁末班车事件中相识,这里得说下日本这种通宵营业的店好像挺不错的,看着也挺正常,国内估计只有酒吧之类的可以。晚上去的地方是有点暗暗的,好像也有点类似酒吧,旁边有类似于 dj 那种,然后同桌的还有除了男女主的另外一对男女,也是因为没赶上地铁末班车的,但也是陌生人,然后小麦突然看到了有个非常有名的电影人,小娟竟然也认识,然后旁边那对完全不认识,还在那吹自己看过很多电影,比如《肖申克的救赎》,于是男女主都特别鄙夷地看着他们,然后他们又去了另一个有点像泡澡的地方席地而坐,他们发现了自己的鞋子都是一样的,然后在女的去上厕所的时候,小麦暗恋的学姐也来了,然后小麦就去跟学姐他们一起坐了,小娟回来后有点不开心就说去朋友家睡,幸好小麦看出来了(他竟然看出来了,本来以为应该是没填过恋爱很木讷的),就追出去,然后就去了小麦家,到了家小娟发现小麦家的书柜上的书简直就跟她自己家的一模一样,小麦还给小娟吹了头发,一起吃烤饭团,看电影,第二天送小娟上了公交,还约好了一起看木乃伊展,然而并没有交换联系方式,但是他们还是约上了一起看了木乃伊展,在餐馆就出现了片头那一幕的来源,因为餐馆他们想一起听歌,就用有线耳机一人一个耳朵听,但是旁边就有个大叔说“你们是不是不爱音乐,左右耳朵是不一样的,只有一起听才是真正的音乐”这样的话,然后的剧情有点跳,因为是指他们一直在这家餐馆吃饭,中间有他们一起出去玩情节穿插着,也是在这他们确立了关系,可以说主体就是体现了他们非常的合拍和默契,就像一些影评说的,这部电影是说如何跟百分百合拍的人分手,然后就是正常的恋爱开始啪啪啪,一直腻在床上,也没去就业说明会,后面也有讲了一点小麦带着小娟去认识他的朋友,也把小娟介绍给了他们认识,这里算是个小伏笔,后面他们分手也有这里的人的一些关系,接下去的剧情说实话我是不太喜欢的,如果一部八分的电影只是说恋爱被现实打败的话,我觉得在我这是不合格的,但是事实也是这样,小麦其实是有家里的资助,所以后面还是按自己的喜好给一些机构画点插画,小娟则要出去工作,因为小娟家庭观念也是要让她出去有正经工作,用脚指头想也能知道肯定不顺利,然后就是暂时在一家蛋糕店工作,小麦就每天去接小娟,日子过得甜甜蜜蜜,后面小娟在自己的努力下考了个什么资格证,去了一家医院还是什么做前台行政,这中间当然就有父母来见面吃饭了,他们在开始恋爱不久就同居合租了,然后小娟父母就是来说要让她有个正经工作,对男的说的话就是人生就是责任这类的话,而小麦爸爸算是个导火索,因为小麦家里是做烟花生意的,他爸让他就做烟花生意,因为要回老家,并且小麦也不想做,所以就拒绝了,然后他爸就说不给他每个月五万的资助,这也导致了小麦需要去找工作,这个过程也是很辛苦,本来想要年前找好工作,然后事与愿违,后面有一次小娟被同事吐槽怎么从来不去团建,于是她就去了(我以为会拒绝),正在团建的时候小麦给她电话,说找到工作了,是一个创业物流公司这种,这里剧情就是我觉得比较俗套的,小麦各种被虐,累成狗,但是就像小娟爸爸说的话,人生就是责任,所以一直在坚持,但是这样也导致了跟小娟的交流也越来越少,他们原来最爱的漫画,爱玩的游戏,也只剩小娟一个人看,一个人玩,而正是这个时候,小娟说她辞掉了工作,去做一个不是太靠谱的漫画改造的密室逃脱,然后这里其实有一点后面争议很大的,就是这个工作其实是前面小麦介绍给小娟的那些朋友中一个的女朋友介绍的,而在有个剧情就是小娟有一次在这个密室逃脱的老板怀里醒过来,是在 KTV 那样的场景里,这就有很多人觉得小娟是不是出轨了,我觉得其实不那么重要,因为这个离职的事情已经让一切矛盾都摆在眼前,小麦其实是接受这种需要承担责任的生活,也想着要跟小娟结婚,但是小娟似乎还是想要过着那样理想的生活,做自己想做的事情,看自己爱看的漫画,也要小麦能像以前那样一直那么默契的有着相同的爱好,这里的触发点其实还有个是那个小麦的朋友(也就是他女朋友介绍小娟那个不靠谱工作的)的葬礼上,小麦在参加完葬礼后有挺多想倾诉的,而小娟只是想睡了,这个让小麦第二天起来都不想理小娟,只是这里我不太理解,难道这点闹情绪都不能接受吗,所谓的合拍也只是毫无限制的情况下的合拍吧,真正的生活怎么可能如此理想呢,即使没有物质生活的压力,也会有其他的各种压力和限制,在这之后其实小麦想说的是小娟是不是没有想跟自己继续在一起的想法了,而小娟觉得都不说话了,还怎么结婚呢,后面其实导演搞了个小 trick,突然放了异常婚礼,但是不是男女主的,我并不觉得这个桥段很好,在婚礼里男女主都觉得自己想要跟对方说分手了,但是当他们去了最开始一直去的餐馆的时候,一个算是一个现实映照的就是他们一直坐的位子被占了,可能也是导演想通过这个来说明他们已经回不去了,在餐馆交谈的时候,小麦其实是说他们结婚吧,并没有想前面婚礼上预设地要分手,但是小娟放弃了,不想结婚,因为不想过那样的生活了,而小麦觉得可能生活就是那样,不可能一直保持刚恋爱时候的那种感觉,生活就是责任,人生就意味着责任。 - -我的一些观点也在前面说了,恋爱到婚姻,即使物质没问题,经济没问题,也会有各种各样的问题,需要一起去解决,因为结婚就意味着需要相互扶持,而不是各取所需,可能我的要求比较高,后面男女主在分手后还一起住了一段时间,我原来还在想会不会通过这个方式让他们继续去磨合同步,只是我失望了,最后给个打分可能是 5 到 6 分吧,勉强及格,好的影视剧应该源于生活高于生活,这一部可能还比不上生活。
-]]>- -生活 -- -生活 -看剧 -- diff --git a/sitemap.xml b/sitemap.xml index 2c776728ff..a7ab834f80 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1435,7 +1435,7 @@聊聊这次换车牌及其他 -/2022/02/20/%E8%81%8A%E8%81%8A%E8%BF%99%E6%AC%A1%E6%8D%A2%E8%BD%A6%E7%89%8C%E5%8F%8A%E5%85%B6%E4%BB%96/ -去年 8 月份运气比较好,摇到了车牌,本来其实应该很早就开始摇的,前面第一次换工作没注意社保断缴了一个月,也是大意失荆州,后面到了 17 年社保满两年了,好像只摇了一次,还是就没摇过,有点忘了,好像是什么原因导致那次也没摇成功,但是后面暂住证就取消了,需要居住证,居住证又要一年及以上的租房合同,并且那会买车以后也不怎么开,住的地方车位还好,但是公司车位一个月要两三千,甚至还是打车上下班比较实惠,所以也没放在心上,后面摇到房以后,也觉得应该准备起来车子,就开始办了居住证,居住证其实还可以用劳动合同,而且办起来也挺快,大概是三四月份开始摇,到 8 月份的某一天收到短信说摇到了,一开始还挺开心,不过心里抱着也不怎么开,也没怎么大放在心上,不过这里有一点就是我把那个照片直接发出去,上面有着我的身份证号,被 LD 说了一顿,以后也应该小心点,但是后面不知道是哪里看了下,说杭州上牌已经需要国六标准的车了,瞬间感觉是空欢喜了,可是有同事说是可以的,我就又打了官方的电话,结果说可以的,要先转籍,然后再做上牌。 - 转籍其实是很方便的,在交警 12123 App 上申请就行了,在转籍以后,需要去实地验车,验车的话,在支付宝-杭州交警生活号里进行预约,找就近的车管所就好,需要准备一些东西,首先是行驶证,机动车登记证书,身份证,居住证,还有车上需要准备的东西是要有三脚架和反光背心,反光背心是最近几个月开始要的,问过之前去验车的只需要三脚架就好了,预约好了的话建议是赶上班时间越早越好,不然过去排队时间要很久,而且人多了以后会很乱,各种插队,而且有很多都是汽车销售,一个销售带着一堆车,我们附近那个进去的小路没一会就堵满车,进去需要先排队,然后扫码,接着交资料,这两个都排着队,如果去晚了就要排很久的队,交完资料才是排队等验车,验车就是打开引擎盖,有人会帮忙拓印发动机车架号,然后验车的会各种检查一下,车里面,还有后备箱,建议车内整理干净点,后备箱不要放杂物,检验完了之后,需要把三脚架跟反光背心放在后备箱盖子上,人在旁边拍个照,然后需要把车牌遮住后再拍个车子的照片,再之后就是去把车牌卸了,这个多吐槽下,那边应该是本来那边师傅帮忙卸车牌,结果他就说是教我们拆,虽然也不算难,但是不排除师傅有在偷懒,完了之后就是把旧车牌交回去,然后需要在手机上(警察叔叔 App)提交各种资料,包括身份证,行驶证,机动车登记证书,提交了之后就等寄车牌过来了。
-这里面缺失的一个环节就是选号了,选号杭州有两个方式,一种就是根据交管局定期发布的选号号段,可以自定义拼 20 个号,在手机上的交警 12123 App 上可以三个一组的形式提交,如果有没被选走的,就可以预选到这个了,但是这种就是也需要有一定策略,最新出的号段能选中的概率大一点,然后数字全是 8,6 这种的肯定会一早就被选走,然后如果跟我一样可以提前选下尾号,因为尾号数字影响限号,我比较有可能周五回家,所以得避开 5,0 的,第二种就是 50 选一跟以前新车选号一样,就不介绍了。第一种选中了以后可以在前面交还旧车牌的时候填上等着寄过来了,因为我是第一种选中的,第二种也可以在手机上选,也在可以在交还车牌的时候现场选。
-总体过程其实是 LD 在各种查资料跟帮我跑来跑去,要不是 LD,估计在交管局那边我就懵逼了,各种插队,而且车子开着车子,也不能随便跑,所以建议办这个的时候有个人一起比较好。
+聊聊Java中的单例模式 +/2019/12/21/%E8%81%8A%E8%81%8AJava%E4%B8%AD%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/ +这是个 Java 面试的高频问题,我也遇到过,以往都是觉得这类题没意思,网上一搜一大堆,也不愿意记,其实说回来,主要还是没静下心来好好去理解,今天无意中看到一个课程,基本帮我把一些疑惑的点讲清楚了,首先单例是啥意思,这个其实是有范围一说,比如我起了个 Spring Boot应用,在这个应用范围内,我的常规 bean 是单例的,意味着 getBean 的时候其实永远只会拿到那一个对象,那要怎么来写一个单例呢,首先就是传说中的饿汉模式,也是最简单的 +饿汉模式
+public class Singleton1 { + // 首先,将构造方法变成私有的 + private Singleton1() {}; + // 创建私有静态实例,这样第一次使用的时候就会进行创建 + private static Singleton instance = new Singleton1(); + + // 使用这个对象都是通过这个 getInstance 来获取 + public static Singleton1 getInstance() { + return instance; + } + // 瞎写一个静态方法。这里想说的是,如果我们只是要调用 Singleton.getDate(...), + // 本来是不想要生成 Singleton 实例的,不过没办法,已经生成了 + public static Date getDate(String mode) {return new Date();} +}上面借鉴了一些代码,其实这是最基本,也不会错的方法,但是正如其中
+getDate方法里说的问题,有时候并没有想那这个对象,但是因为我调用了这个类的静态方法,导致对象已经生成了,可能这也是饿汉模式名字的来由,不管三七二十一给你生成个单例就完事了,不管有没有用,但是这种个人觉得也没啥大问题,如果是面试的话最好说出来它的缺点饱汉模式
+public class Singleton2 { + // 首先,也是先堵死 new Singleton() 这条路,将构造方法变成私有 + private Singleton2() {} + // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的 + private static volatile Singleton2 instance = null; + + private int m = 9; + + public static Singleton getInstance() { + if (instance == null) { + // 加锁 + synchronized (Singleton2.class) { + // 这一次判断也是必须的,不然会有并发问题 + if (instance == null) { + instance = new Singleton2(); + } + } + } + return instance; + } +}这里容易错的有三点,理解了其实就比较好记了
+第一点,为啥不在 getInstance 上整个代码块加
+synchronized,这个其实比较容易理解,就是锁的力度太大,性能太差了,这点其实也要去理解,可以举个夸张的例子,比如我一个电商的服务,如果为了避免一个人的订单出现问题,是不是可以从请求入口就把他锁住,到请求结束释放,那么里面做的事情都有保障,然而这显然不可能,因为我们想要这种竞态条件抢占资源的时间尽量减少,防止其他线程等待。
第二点,为啥synchronized之已经检查了instance == null,还要在里面再检查一次,这个有个术语,叫double check lock,但是为啥要这么做呢,其实很简单,想象当有两个线程,都过了第一步为空判断,这个时候只有一个线程能拿到这个锁,另一个线程就等待了,如果不再判断一次,那么第一个线程新建完对象释放锁之后,第二个线程又能拿到锁,再去创建一个对象。
第三点,为啥要volatile关键字,原先对它的理解是它修饰的变量在 JMM 中能及时将变量值写到主存中,但是它还有个很重要的作用,就是防止指令重排序,instance = new Singleton();这行代码其实在底层是分成三条指令执行的,第一条是在堆上申请了一块内存放这个对象,但是对象的字段啥的都还是默认值,第二条是设置对象的值,比如上面的 m 是 9,然后第三条是将这个对象和虚拟机栈上的指针建立引用关联,那么如果我不用volatile关键字,这三条指令就有可能出现重排,比如变成了 1-3-2 这种顺序,当执行完第二步时,有个线程来访问这个对象了,先判断是不是空,发现不是空的,就拿去直接用了,是不是就出现问题了,所以这个volatile也是不可缺少的嵌套类
+public class Singleton3 { + + private Singleton3() {} + // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性 + private static class Holder { + private static Singleton3 instance = new Singleton3(); + } + public static Singleton3 getInstance() { + return Holder.instance; + } +}这个我个人感觉是饿汉模式的升级版,可以在调用
+getInstance的时候去实例化对象,也是比较推荐的枚举单例
+public enum Singleton { + INSTANCE; + + public void doSomething(){ + //todo doSomething + } +}枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。
]]>- 生活 +Java +Design Patterns +Singleton - 生活 -换车牌 +设计模式 +Design Patterns +单例 +Singleton @@ -1443,1904 +1443,1904 @@https://nicksxs.me/ -2022-03-27 +2022-04-02 daily 1.0 https://nicksxs.me/tags/%E7%94%9F%E6%B4%BB/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Java/ -2022-03-27 +https://nicksxs.me/tags/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/JVM/ -2022-03-27 +https://nicksxs.me/tags/2020/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/C/ -2022-03-27 +https://nicksxs.me/tags/2021/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ -2022-03-27 +https://nicksxs.me/tags/%E6%8B%96%E6%9B%B4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/2020/ -2022-03-27 +https://nicksxs.me/tags/Java/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/2021/ -2022-03-27 +https://nicksxs.me/tags/JVM/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%8B%96%E6%9B%B4/ -2022-03-27 +https://nicksxs.me/tags/C/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/leetcode/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/java/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Binary-Tree/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/DFS/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E4%BA%8C%E5%8F%89%E6%A0%91/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E9%A2%98%E8%A7%A3/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Linked-List/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%AF%BB%E5%90%8E%E6%84%9F/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/ -2022-03-27 +https://nicksxs.me/tags/2019/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/2019/ -2022-03-27 +https://nicksxs.me/tags/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/c/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%8A%80%E6%9C%AF/ -2022-03-27 +https://nicksxs.me/tags/%E5%B9%B6%E5%8F%91/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E8%AF%BB%E4%B9%A6/ -2022-03-27 +https://nicksxs.me/tags/j-u-c/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%B9%B6%E5%8F%91/ -2022-03-27 +https://nicksxs.me/tags/aqs/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/j-u-c/ -2022-03-27 +https://nicksxs.me/tags/condition/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/aqs/ -2022-03-27 +https://nicksxs.me/tags/await/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/condition/ -2022-03-27 +https://nicksxs.me/tags/signal/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/await/ -2022-03-27 +https://nicksxs.me/tags/lock/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/signal/ -2022-03-27 +https://nicksxs.me/tags/unlock/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/lock/ -2022-03-27 +https://nicksxs.me/tags/%E6%8A%80%E6%9C%AF/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/unlock/ -2022-03-27 +https://nicksxs.me/tags/%E8%AF%BB%E4%B9%A6/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Apollo/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/value/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%B3%A8%E8%A7%A3/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/environment/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Stream/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Comparator/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%8E%92%E5%BA%8F/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/sort/ -2022-03-27 +2022-04-02 weekly 0.2 + +https://nicksxs.me/tags/nullsfirst/ -2022-03-27 +2022-04-02 +weekly +0.2 ++ https://nicksxs.me/tags/Disruptor/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Filter/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Interceptor/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/AOP/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Spring/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Tomcat/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Servlet/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Web/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/G1/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/GC/ -2022-03-27 +2022-04-02 weekly 0.2 - -https://nicksxs.me/tags/Garbage-First-Collector/ -2022-03-27 -weekly -0.2 -- https://nicksxs.me/tags/Disruptor/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E9%80%92%E5%BD%92/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Preorder-Traversal/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Inorder-Traversal/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%89%8D%E5%BA%8F/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E4%B8%AD%E5%BA%8F/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/DP/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/stack/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/min-stack/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%9C%80%E5%B0%8F%E6%A0%88/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/leetcode-155/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/linked-list/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Lowest-Common-Ancestor-of-a-Binary-Tree/ -2022-03-27 +2022-04-02 weekly 0.2 - -https://nicksxs.me/tags/string/ -2022-03-27 -weekly -0.2 -- https://nicksxs.me/tags/Intersection-of-Two-Arrays/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Median-of-Two-Sorted-Arrays/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/dp/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E4%BB%A3%E7%A0%81%E9%A2%98%E8%A7%A3/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Trapping-Rain-Water/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%8E%A5%E9%9B%A8%E6%B0%B4/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Leetcode-42/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Rotate-Image/ -2022-03-27 +2022-04-02 weekly 0.2 + +https://nicksxs.me/tags/%E7%9F%A9%E9%98%B5/ -2022-03-27 +2022-04-02 +weekly +0.2 ++ https://nicksxs.me/tags/Intersection-of-Two-Arrays/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Remove-Duplicates-from-Sorted-List/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/linux/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/grep/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%BD%AC%E4%B9%89/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/mfc/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Maven/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/C/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Redis/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Distributed-Lock/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/hadoop/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/cluster/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/docker/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/mysql/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Docker/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/namespace/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/cgroup/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Dockerfile/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/echo/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/uname/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%8F%91%E8%A1%8C%E7%89%88/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Gogs/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Webhook/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%8D%9A%E5%AE%A2%EF%BC%8C%E6%96%87%E7%AB%A0/ -2022-03-27 +https://nicksxs.me/tags/Mysql/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Mysql/ -2022-03-27 +https://nicksxs.me/tags/Mybatis/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Mybatis/ -2022-03-27 +https://nicksxs.me/tags/Sql%E6%B3%A8%E5%85%A5/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Sql%E6%B3%A8%E5%85%A5/ -2022-03-27 +https://nicksxs.me/tags/%E5%8D%9A%E5%AE%A2%EF%BC%8C%E6%96%87%E7%AB%A0/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E7%BC%93%E5%AD%98/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/openresty/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/nginx/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/php/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/mq/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/im/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/redis/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%BA%90%E7%A0%81/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%BA%94%E7%94%A8/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Evict/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%BF%87%E6%9C%9F%E7%AD%96%E7%95%A5/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Rust/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%89%80%E6%9C%89%E6%9D%83/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%86%85%E5%AD%98%E5%88%86%E5%B8%83/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%96%B0%E8%AF%AD%E8%A8%80/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%8F%AF%E5%8F%98%E5%BC%95%E7%94%A8/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E4%B8%8D%E5%8F%AF%E5%8F%98%E5%BC%95%E7%94%A8/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%88%87%E7%89%87/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/spark/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/python/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Spring-Event/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/websocket/ -2022-03-27 +https://nicksxs.me/tags/WordPress/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/swoole/ -2022-03-27 +https://nicksxs.me/tags/%E5%B0%8F%E6%8A%80%E5%B7%A7/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/WordPress/ -2022-03-27 +https://nicksxs.me/tags/websocket/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%B0%8F%E6%8A%80%E5%B7%A7/ -2022-03-27 +https://nicksxs.me/tags/swoole/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/gc/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/jvm/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/MQ/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/RocketMQ/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%89%8A%E5%B3%B0%E5%A1%AB%E8%B0%B7/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E4%B8%AD%E9%97%B4%E4%BB%B6/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/ssh/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%90%90%E6%A7%BD/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E7%96%AB%E6%83%85/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E7%BE%8E%E5%9B%BD/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%85%AC%E4%BA%A4%E8%BD%A6/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%8F%A3%E7%BD%A9/ -2022-03-27 +2022-04-02 weekly 0.2 + +https://nicksxs.me/tags/%E6%9D%80%E4%BA%BA%E8%AF%9B%E5%BF%83/ -2022-03-27 +2022-04-02 +weekly +0.2 ++ + +https://nicksxs.me/tags/%E6%89%93%E5%8D%A1/ +2022-04-02 +weekly +0.2 ++ + +https://nicksxs.me/tags/%E5%B9%B8%E7%A6%8F%E4%BA%86%E5%90%97/ +2022-04-02 +weekly +0.2 ++ https://nicksxs.me/tags/%E8%B6%B3%E7%90%83/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%BC%80%E8%BD%A6/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%8A%A0%E5%A1%9E/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E7%B3%9F%E5%BF%83%E4%BA%8B/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%A7%84%E5%88%99/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%85%AC%E4%BA%A4/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%B7%AF%E6%94%BF%E8%A7%84%E5%88%92/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E6%9D%AD%E5%B7%9E/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%81%A5%E5%BA%B7%E7%A0%81/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%89%93%E5%8D%A1/ -2022-03-27 +https://nicksxs.me/tags/%E8%BF%90%E5%8A%A8/ +2022-04-02 weekly 0.2 - - -https://nicksxs.me/tags/%E5%B9%B8%E7%A6%8F%E4%BA%86%E5%90%97/ -2022-03-27 -weekly -0.2 -- - -https://nicksxs.me/tags/%E8%B6%B3%E7%90%83/ -2022-03-27 -weekly -0.2 -- - -https://nicksxs.me/tags/%E8%BF%90%E5%8A%A8/ -2022-03-27 -weekly -0.2 -- https://nicksxs.me/tags/%E5%87%8F%E8%82%A5/ -2022-03-27 +https://nicksxs.me/tags/%E5%87%8F%E8%82%A5/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%B7%91%E6%AD%A5/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%B9%B2%E6%B4%BB/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%BD%B1%E8%AF%84/ -2022-03-27 +https://nicksxs.me/tags/scp/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%AF%84%E7%94%9F%E8%99%AB/ -2022-03-27 +https://nicksxs.me/tags/git/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/git/ -2022-03-27 +https://nicksxs.me/tags/%E5%BD%B1%E8%AF%84/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/DefaultMQPushConsumer/ -2022-03-27 +https://nicksxs.me/tags/%E5%AF%84%E7%94%9F%E8%99%AB/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/ -2022-03-27 +https://nicksxs.me/tags/%E5%AD%97%E7%AC%A6%E9%9B%86/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/NameServer/ -2022-03-27 +https://nicksxs.me/tags/%E7%BC%96%E7%A0%81/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/scp/ -2022-03-27 +https://nicksxs.me/tags/utf8/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/SpringBoot/ -2022-03-27 +https://nicksxs.me/tags/utf8mb4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/cglib/ -2022-03-27 +https://nicksxs.me/tags/utf8mb4-0900-ai-ci/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%BA%8B%E5%8A%A1/ -2022-03-27 +https://nicksxs.me/tags/utf8mb4-unicode-ci/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Druid/ -2022-03-27 +https://nicksxs.me/tags/utf8mb4-general-ci/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%95%B0%E6%8D%AE%E6%BA%90%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2/ -2022-03-27 +https://nicksxs.me/tags/DefaultMQPushConsumer/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%B8%9C%E4%BA%AC%E5%A5%A5%E8%BF%90%E4%BC%9A/ -2022-03-27 +https://nicksxs.me/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%B8%BE%E9%87%8D/ -2022-03-27 +https://nicksxs.me/tags/NameServer/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%B0%84%E5%87%BB/ -2022-03-27 +https://nicksxs.me/tags/SpringBoot/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%B9%92%E4%B9%93%E7%90%83/ -2022-03-27 +https://nicksxs.me/tags/cglib/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E8%B7%B3%E6%B0%B4/ -2022-03-27 +https://nicksxs.me/tags/%E4%BA%8B%E5%8A%A1/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Dubbo/ -2022-03-27 +https://nicksxs.me/tags/Druid/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/RPC/ -2022-03-27 +https://nicksxs.me/tags/%E6%95%B0%E6%8D%AE%E6%BA%90%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/SPI/ -2022-03-27 +https://nicksxs.me/tags/%E4%B8%9C%E4%BA%AC%E5%A5%A5%E8%BF%90%E4%BC%9A/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Adaptive/ -2022-03-27 +https://nicksxs.me/tags/%E4%B9%92%E4%B9%93%E7%90%83/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E8%87%AA%E9%80%82%E5%BA%94%E6%8B%93%E5%B1%95/ -2022-03-27 +https://nicksxs.me/tags/%E8%B7%B3%E6%B0%B4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/ -2022-03-27 +https://nicksxs.me/tags/%E4%B8%BE%E9%87%8D/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%AD%97%E7%AC%A6%E9%9B%86/ -2022-03-27 +https://nicksxs.me/tags/%E5%B0%84%E5%87%BB/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E7%BC%96%E7%A0%81/ -2022-03-27 +https://nicksxs.me/tags/Dubbo/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/utf8/ -2022-03-27 +https://nicksxs.me/tags/RPC/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/utf8mb4/ -2022-03-27 +https://nicksxs.me/tags/SPI/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/utf8mb4-0900-ai-ci/ -2022-03-27 +https://nicksxs.me/tags/Adaptive/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/utf8mb4-unicode-ci/ -2022-03-27 +https://nicksxs.me/tags/%E8%87%AA%E9%80%82%E5%BA%94%E6%8B%93%E5%B1%95/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/utf8mb4-general-ci/ -2022-03-27 +https://nicksxs.me/tags/%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Synchronized/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%81%8F%E5%90%91%E9%94%81/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E9%87%8D%E9%87%8F%E7%BA%A7%E9%94%81/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%87%AA%E6%97%8B/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E7%B1%BB%E5%8A%A0%E8%BD%BD/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%8A%A0%E8%BD%BD/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E9%AA%8C%E8%AF%81/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%87%86%E5%A4%87/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E8%A7%A3%E6%9E%90/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%88%9D%E5%A7%8B%E5%8C%96/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E9%93%BE%E6%8E%A5/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/JPS/ -2022-03-27 +https://nicksxs.me/tags/top/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/JStack/ -2022-03-27 +https://nicksxs.me/tags/Broker/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/JMap/ -2022-03-27 +https://nicksxs.me/tags/JPS/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/top/ -2022-03-27 +https://nicksxs.me/tags/JStack/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Broker/ -2022-03-27 +https://nicksxs.me/tags/JMap/ +2022-04-02 weekly 0.2 https://nicksxs.me/tags/Sharding-Jdbc/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/ThreadPool/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E7%BA%BF%E7%A8%8B%E6%B1%A0/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/FixedThreadPool/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/LimitedThreadPool/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/EagerThreadPool/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/CachedThreadPool/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/mvcc/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/read-view/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/gap-lock/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/next-key-lock/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/tags/%E5%B9%BB%E8%AF%BB/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F/ -2022-03-27 +https://nicksxs.me/tags/%E7%B4%A2%E5%BC%95/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF/ -2022-03-27 +https://nicksxs.me/tags/is-null/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9/ -2022-03-27 +https://nicksxs.me/tags/is-not-null/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/ -2022-03-27 +https://nicksxs.me/tags/procedure/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/bloom-filter/ -2022-03-27 +https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%BA%92%E6%96%A5%E9%94%81/ -2022-03-27 +https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E7%B4%A2%E5%BC%95/ -2022-03-27 +https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/is-null/ -2022-03-27 +https://nicksxs.me/tags/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/is-not-null/ -2022-03-27 +https://nicksxs.me/tags/bloom-filter/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/procedure/ -2022-03-27 +https://nicksxs.me/tags/%E4%BA%92%E6%96%A5%E9%94%81/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/ -2022-03-27 +https://nicksxs.me/tags/%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Design-Patterns/ -2022-03-27 +https://nicksxs.me/tags/AutoConfiguration/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%8D%95%E4%BE%8B/ -2022-03-27 +https://nicksxs.me/tags/Mac/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Singleton/ -2022-03-27 +https://nicksxs.me/tags/PHP/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/ -2022-03-27 +https://nicksxs.me/tags/Homebrew/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/AutoConfiguration/ -2022-03-27 +https://nicksxs.me/tags/icu4c/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/ThreadLocal/ -2022-03-27 +https://nicksxs.me/tags/zsh/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%BC%B1%E5%BC%95%E7%94%A8/ -2022-03-27 +https://nicksxs.me/tags/%E6%97%85%E6%B8%B8/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/ -2022-03-27 +https://nicksxs.me/tags/%E5%8E%A6%E9%97%A8/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/WeakReference/ -2022-03-27 +https://nicksxs.me/tags/%E4%B8%AD%E5%B1%B1%E8%B7%AF/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Mac/ -2022-03-27 +https://nicksxs.me/tags/%E5%B1%80%E5%8F%A3%E8%A1%97/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/PHP/ -2022-03-27 +https://nicksxs.me/tags/%E9%BC%93%E6%B5%AA%E5%B1%BF/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Homebrew/ -2022-03-27 +https://nicksxs.me/tags/%E6%9B%BE%E5%8E%9D%E5%9E%B5/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/icu4c/ -2022-03-27 +https://nicksxs.me/tags/%E6%A4%8D%E7%89%A9%E5%9B%AD/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/zsh/ -2022-03-27 +https://nicksxs.me/tags/%E9%A9%AC%E6%88%8F%E5%9B%A2/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%97%85%E6%B8%B8/ -2022-03-27 +https://nicksxs.me/tags/%E6%B2%99%E8%8C%B6%E9%9D%A2/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%8E%A6%E9%97%A8/ -2022-03-27 +https://nicksxs.me/tags/%E6%B5%B7%E8%9B%8E%E7%85%8E/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%B8%AD%E5%B1%B1%E8%B7%AF/ -2022-03-27 +https://nicksxs.me/tags/ThreadLocal/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%B1%80%E5%8F%A3%E8%A1%97/ -2022-03-27 +https://nicksxs.me/tags/%E5%BC%B1%E5%BC%95%E7%94%A8/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E9%BC%93%E6%B5%AA%E5%B1%BF/ -2022-03-27 +https://nicksxs.me/tags/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%9B%BE%E5%8E%9D%E5%9E%B5/ -2022-03-27 +https://nicksxs.me/tags/WeakReference/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%A4%8D%E7%89%A9%E5%9B%AD/ -2022-03-27 +https://nicksxs.me/tags/%E6%89%B6%E6%A2%AF/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E9%A9%AC%E6%88%8F%E5%9B%A2/ -2022-03-27 +https://nicksxs.me/tags/%E8%B8%A9%E8%B8%8F/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%B2%99%E8%8C%B6%E9%9D%A2/ -2022-03-27 +https://nicksxs.me/tags/%E5%AE%89%E5%85%A8/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%B5%B7%E8%9B%8E%E7%85%8E/ -2022-03-27 +https://nicksxs.me/tags/%E7%94%B5%E7%93%B6%E8%BD%A6/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E6%89%B6%E6%A2%AF/ -2022-03-27 +https://nicksxs.me/tags/Thread-dump/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E8%B8%A9%E8%B8%8F/ -2022-03-27 +https://nicksxs.me/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%AE%89%E5%85%A8/ -2022-03-27 +https://nicksxs.me/tags/%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E7%94%B5%E7%93%B6%E8%BD%A6/ -2022-03-27 +https://nicksxs.me/tags/%E4%B8%89%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/ -2022-03-27 +https://nicksxs.me/tags/2PC/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/ -2022-03-27 +https://nicksxs.me/tags/3PC/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%B8%89%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/ -2022-03-27 +https://nicksxs.me/tags/%E9%AA%91%E8%BD%A6/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/2PC/ -2022-03-27 +https://nicksxs.me/tags/%E7%9C%8B%E5%89%A7/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/3PC/ -2022-03-27 +https://nicksxs.me/tags/%E8%A3%85%E7%94%B5%E8%84%91/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/Thread-dump/ -2022-03-27 +https://nicksxs.me/tags/%E8%80%81%E7%94%B5%E8%84%91/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E9%AA%91%E8%BD%A6/ -2022-03-27 +https://nicksxs.me/tags/360-%E5%85%A8%E5%AE%B6%E6%A1%B6/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E7%9C%8B%E5%89%A7/ -2022-03-27 +https://nicksxs.me/tags/%E4%BF%AE%E7%94%B5%E8%84%91%E7%9A%84/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E8%A3%85%E7%94%B5%E8%84%91/ -2022-03-27 +https://nicksxs.me/tags/%E6%8D%A2%E8%BD%A6%E7%89%8C/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E8%80%81%E7%94%B5%E8%84%91/ -2022-03-27 +https://nicksxs.me/tags/%E7%9C%8B%E4%B9%A6/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/360-%E5%85%A8%E5%AE%B6%E6%A1%B6/ -2022-03-27 +https://nicksxs.me/tags/%E9%AB%98%E9%80%9F/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E4%BF%AE%E7%94%B5%E8%84%91%E7%9A%84/ -2022-03-27 +https://nicksxs.me/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E7%9C%8B%E4%B9%A6/ -2022-03-27 +https://nicksxs.me/tags/Design-Patterns/ +2022-04-02 weekly 0.2 - https://nicksxs.me/tags/%E9%AB%98%E9%80%9F/ -2022-03-27 +https://nicksxs.me/tags/%E5%8D%95%E4%BE%8B/ +2022-04-02 weekly 0.2 - @@ -3349,1008 +3349,1008 @@https://nicksxs.me/tags/%E6%8D%A2%E8%BD%A6%E7%89%8C/ -2022-03-27 +https://nicksxs.me/tags/Singleton/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/JVM/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/leetcode/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2020/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/GC/ -2022-03-27 +2022-04-02 weekly 0.2 - -https://nicksxs.me/categories/Binary-Tree/ -2022-03-27 -weekly -0.2 -- https://nicksxs.me/categories/Linked-List/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/C/ -2022-03-27 +https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/leetcode/java/ -2022-03-27 +https://nicksxs.me/categories/Linked-List/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2019/ +2022-04-02 weekly 0.2 - -https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/ -2022-03-27 -weekly -0.2 -- https://nicksxs.me/categories/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2020/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2019/ -2022-03-27 +https://nicksxs.me/categories/C/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/%E5%B9%B6%E5%8F%91/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/leetcode/java/Binary-Tree/ -2022-03-27 +https://nicksxs.me/categories/leetcode/java/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/java/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91/ -2022-03-27 +https://nicksxs.me/categories/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2020/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/Apollo/ -2022-03-27 +https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/leetcode/java/Linked-List/ -2022-03-27 +https://nicksxs.me/categories/Java/Apollo/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/%E9%9B%86%E5%90%88/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/2020/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Filter/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/leetcode/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/2021/ -2022-03-27 +https://nicksxs.me/categories/leetcode/java/Binary-Tree/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/DP/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/leetcode/java/Binary-Tree/DFS/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/2021/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/stack/ -2022-03-27 +2022-04-02 weekly 0.2 + +https://nicksxs.me/categories/linked-list/ -2022-03-27 +2022-04-02 +weekly +0.2 ++ https://nicksxs.me/categories/leetcode/java/Linked-List/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E5%AD%97%E7%AC%A6%E4%B8%B2-online/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/Apollo/value/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Linux/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/Maven/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Redis/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Interceptor-AOP/ -2022-03-27 +https://nicksxs.me/categories/data-analysis/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/data-analysis/ -2022-03-27 +https://nicksxs.me/categories/Interceptor-AOP/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/docker/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Docker/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/leetcode/java/DP/ -2022-03-27 +https://nicksxs.me/categories/leetcode/java/Binary-Tree/DFS/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/Java/Mybatis/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/nginx/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/php/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/redis/ -2022-03-27 +https://nicksxs.me/categories/leetcode/java/DP/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/leetcode/java/stack/ -2022-03-27 +https://nicksxs.me/categories/redis/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/leetcode/Lowest-Common-Ancestor-of-a-Binary-Tree/ -2022-03-27 +https://nicksxs.me/categories/leetcode/java/stack/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/leetcode/java/linked-list/ -2022-03-27 +https://nicksxs.me/categories/Java/leetcode/Lowest-Common-Ancestor-of-a-Binary-Tree/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E8%AF%AD%E8%A8%80/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/Spring/ -2022-03-27 +https://nicksxs.me/categories/leetcode/java/linked-list/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/leetcode/java/string/ -2022-03-27 +https://nicksxs.me/categories/Java/Spring/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/leetcode/Rotate-Image/ -2022-03-27 +https://nicksxs.me/categories/Java/gc/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/gc/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/MQ/ -2022-03-27 +2022-04-02 weekly 0.2 https://nicksxs.me/categories/ssh/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/ -2022-03-27 +https://nicksxs.me/categories/leetcode/java/string/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%85%AC%E4%BA%A4/ -2022-03-27 +https://nicksxs.me/categories/Java/leetcode/Rotate-Image/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%99%BD%E5%B2%A9%E6%9D%BE/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%85%AC%E4%BA%A4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Redis/Distributed-Lock/ -2022-03-27 +https://nicksxs.me/categories/shell/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BD%B1%E8%AF%84/ -2022-03-27 +https://nicksxs.me/categories/git/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/git/ -2022-03-27 +https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Spring/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BD%B1%E8%AF%84/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Docker/%E4%BB%8B%E7%BB%8D/ -2022-03-27 +https://nicksxs.me/categories/Redis/Distributed-Lock/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/shell/ -2022-03-27 +https://nicksxs.me/categories/Spring/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/SpringBoot/ -2022-03-27 +https://nicksxs.me/categories/Docker/%E4%BB%8B%E7%BB%8D/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo-RPC-SPI/ -2022-03-27 +https://nicksxs.me/categories/Mysql/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mysql/ -2022-03-27 +https://nicksxs.me/categories/Mybatis/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo-RPC/ -2022-03-27 +https://nicksxs.me/categories/Redis/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mybatis/ -2022-03-27 +https://nicksxs.me/categories/%E8%AF%AD%E8%A8%80/Rust/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/%E7%B1%BB%E5%8A%A0%E8%BD%BD/ -2022-03-27 +https://nicksxs.me/categories/Rust/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Thread-dump/ -2022-03-27 +https://nicksxs.me/categories/Java/gc/jvm/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo-%E7%BA%BF%E7%A8%8B%E6%B1%A0/ -2022-03-27 +https://nicksxs.me/categories/RocketMQ/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Redis/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/ -2022-03-27 +https://nicksxs.me/categories/ssh/%E6%8A%80%E5%B7%A7/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Redis/%E5%BA%94%E7%94%A8/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/Design-Patterns/ -2022-03-27 +https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%99%BD%E5%B2%A9%E6%9D%BE/%E5%B9%B8%E7%A6%8F%E4%BA%86%E5%90%97/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/SpringBoot/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/%E8%B7%91%E6%AD%A5/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mac/ -2022-03-27 +https://nicksxs.me/categories/shell/%E5%B0%8F%E6%8A%80%E5%B7%A7/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E6%97%85%E6%B8%B8/ -2022-03-27 +https://nicksxs.me/categories/git/%E5%B0%8F%E6%8A%80%E5%B7%A7/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/ -2022-03-27 +https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/grep/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BC%80%E8%BD%A6/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BD%B1%E8%AF%84/2020/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E8%AF%AD%E8%A8%80/Rust/ -2022-03-27 +https://nicksxs.me/categories/C/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Rust/ -2022-03-27 +https://nicksxs.me/categories/Spring/Servlet/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/gc/jvm/ -2022-03-27 +https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/echo/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/RocketMQ/ -2022-03-27 +https://nicksxs.me/categories/Mysql/Sql%E6%B3%A8%E5%85%A5/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/ssh/%E6%8A%80%E5%B7%A7/ -2022-03-27 +https://nicksxs.me/categories/Mybatis/%E7%BC%93%E5%AD%98/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/ -2022-03-27 +https://nicksxs.me/categories/Redis/%E6%BA%90%E7%A0%81/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/grep/ -2022-03-27 +https://nicksxs.me/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%99%BD%E5%B2%A9%E6%9D%BE/%E5%B9%B8%E7%A6%8F%E4%BA%86%E5%90%97/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/%E7%BE%8E%E5%9B%BD/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/%E8%B7%91%E6%AD%A5/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/%E5%8F%A3%E7%BD%A9/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/C/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/%E8%B7%91%E6%AD%A5/%E5%B9%B2%E6%B4%BB/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BD%B1%E8%AF%84/2020/ -2022-03-27 +https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/grep/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/git/%E5%B0%8F%E6%8A%80%E5%B7%A7/ -2022-03-27 +https://nicksxs.me/categories/C/Redis/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Spring/Servlet/ -2022-03-27 +https://nicksxs.me/categories/Spring/Servlet/Interceptor/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/MQ/RocketMQ/ -2022-03-27 +https://nicksxs.me/categories/Docker/%E5%8F%91%E8%A1%8C%E7%89%88%E6%9C%AC/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/shell/%E5%B0%8F%E6%8A%80%E5%B7%A7/ -2022-03-27 +https://nicksxs.me/categories/Spring/Mybatis/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/echo/ -2022-03-27 +https://nicksxs.me/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/RocketMQ/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo/ -2022-03-27 +https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/grep/%E6%9F%A5%E6%97%A5%E5%BF%97/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mysql/Sql%E6%B3%A8%E5%85%A5/ -2022-03-27 +https://nicksxs.me/categories/Spring/Servlet/Interceptor/AOP/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mybatis/%E7%BC%93%E5%AD%98/ -2022-03-27 +https://nicksxs.me/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/top/ -2022-03-27 +https://nicksxs.me/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/RocketMQ/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/ -2022-03-27 +https://nicksxs.me/categories/MQ/RocketMQ/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mysql/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/ -2022-03-27 +https://nicksxs.me/categories/MQ/RocketMQ/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Redis/%E6%BA%90%E7%A0%81/ -2022-03-27 +https://nicksxs.me/categories/Java/SpringBoot/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Redis/%E7%BC%93%E5%AD%98/ -2022-03-27 +https://nicksxs.me/categories/Dubbo-RPC-SPI/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mysql/%E7%B4%A2%E5%BC%95/ -2022-03-27 +https://nicksxs.me/categories/Dubbo-RPC/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Java/Singleton/ -2022-03-27 +https://nicksxs.me/categories/Java/%E7%B1%BB%E5%8A%A0%E8%BD%BD/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/PHP/ -2022-03-27 +https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/top/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/ -2022-03-27 +https://nicksxs.me/categories/Thread-dump/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/ -2022-03-27 +https://nicksxs.me/categories/Dubbo/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/%E7%BE%8E%E5%9B%BD/ -2022-03-27 +https://nicksxs.me/categories/Mysql/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/%E5%8F%A3%E7%BD%A9/ -2022-03-27 +https://nicksxs.me/categories/Dubbo-%E7%BA%BF%E7%A8%8B%E6%B1%A0/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/grep/ -2022-03-27 +https://nicksxs.me/categories/Mysql/%E7%B4%A2%E5%BC%95/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/%E8%B7%91%E6%AD%A5/%E5%B9%B2%E6%B4%BB/ -2022-03-27 +https://nicksxs.me/categories/Redis/%E5%BA%94%E7%94%A8/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/C/Redis/ -2022-03-27 +https://nicksxs.me/categories/Mac/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Spring/Servlet/Interceptor/ -2022-03-27 +https://nicksxs.me/categories/SpringBoot/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/MQ/RocketMQ/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/ -2022-03-27 +https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/top/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Docker/%E5%8F%91%E8%A1%8C%E7%89%88%E6%9C%AC/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E6%97%85%E6%B8%B8/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo/SPI/ -2022-03-27 +https://nicksxs.me/categories/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo/%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/ -2022-03-27 +https://nicksxs.me/categories/Dubbo/SPI/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Spring/Mybatis/ -2022-03-27 +https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/top/ -2022-03-27 +https://nicksxs.me/categories/Mysql/%E6%BA%90%E7%A0%81/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E5%B7%A5%E5%85%B7/ -2022-03-27 +https://nicksxs.me/categories/Dubbo/%E7%BA%BF%E7%A8%8B%E6%B1%A0/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mysql/%E6%BA%90%E7%A0%81/ -2022-03-27 +https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BC%80%E8%BD%A6/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo/%E7%BA%BF%E7%A8%8B%E6%B1%A0/ -2022-03-27 +https://nicksxs.me/categories/Dubbo/%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/ -2022-03-27 +https://nicksxs.me/categories/Java/Design-Patterns/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/C/Mysql/ -2022-03-27 +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Mac/Homebrew/ -2022-03-27 +https://nicksxs.me/categories/Redis/%E7%BC%93%E5%AD%98/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/%E4%B8%89%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/ -2022-03-27 +https://nicksxs.me/categories/PHP/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/RocketMQ/ -2022-03-27 +https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/top/%E6%8E%92%E5%BA%8F/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/grep/%E6%9F%A5%E6%97%A5%E5%BF%97/ -2022-03-27 +https://nicksxs.me/categories/%E5%B7%A5%E5%85%B7/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Spring/Servlet/Interceptor/AOP/ -2022-03-27 +https://nicksxs.me/categories/Dubbo/SPI/Adaptive/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo/SPI/Adaptive/ -2022-03-27 +https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/top/%E6%8E%92%E5%BA%8F/ -2022-03-27 +https://nicksxs.me/categories/Dubbo/%E7%BA%BF%E7%A8%8B%E6%B1%A0/ThreadPool/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/Dubbo/%E7%BA%BF%E7%A8%8B%E6%B1%A0/ThreadPool/ -2022-03-27 +https://nicksxs.me/categories/Java/Singleton/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/%E7%A9%BF%E9%80%8F/ -2022-03-27 +https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/PHP/icu4c/ -2022-03-27 +https://nicksxs.me/categories/Mac/Homebrew/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/ -2022-03-27 +https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/%E4%B8%89%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/ +2022-04-02 weekly 0.2 - https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/%E7%A9%BF%E9%80%8F/%E5%87%BB%E7%A9%BF/ -2022-03-27 +https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/%E7%A9%BF%E9%80%8F/ +2022-04-02 weekly 0.2 - + +https://nicksxs.me/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/RocketMQ/ -2022-03-27 +https://nicksxs.me/categories/PHP/icu4c/ +2022-04-02 +weekly +0.2 ++ https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/%E7%A9%BF%E9%80%8F/%E5%87%BB%E7%A9%BF/ +2022-04-02 weekly 0.2 https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/%E7%A9%BF%E9%80%8F/%E5%87%BB%E7%A9%BF/%E9%9B%AA%E5%B4%A9/ -2022-03-27 +2022-04-02 weekly 0.2
- FixedThreadPool:创建一个复用固定个数线程的线程池。
- FixedThreadPool:创建一个复用固定个数线程的线程池。
- FixedThreadPool:创建一个复用固定个数线程的线程池。






































