Java高频面试专题合集解析:
当然在这还有更多整理总结的Java进阶学习笔记和面试题未展示,其中囊括了Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构资料和完整的Java架构学习进阶导图!
更多Java架构进阶资料展示
mainLock 是线程池的主锁,线程执行、线程销毁和线程池停止等都会使用到这把锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
xxxxx
} finally {
mainLock.unlock();
}
如果频繁获取这把锁,会导致原有线程池任务执行性能受到影响
所以,我们应该避免频繁获取这几项参数,这也是不使用线程池任务执行埋点最重要的原因
03
==
监控数据存储
上面的线程池监控指标如果只能支持实时查看,并不能帮忙开发日常排查错误
大部分场景下,生产上的问题发现会有延迟。比如 12:30 出现的问题,业务13:00 进行的反馈
为了更好帮助开发排错,我们需要将线程池的历史运行数据进行存储
说到线程池历史运行数据的存储,使用 时序数据库(TSDB) 是最合适的
但大部分情况下,公司不会为了这一个需求搭建或者采购时序数据库,那就可以使用折中方案,比如说 MySQL、ES 等
我们以 MySQL 为例,his_run_data 历史运行数据表,建表语句如下:
CREATE TABLE his_run_data
(
thread_pool_id
varchar(56) DEFAULT NULL COMMENT ‘线程池ID’,
instance_id
varchar(256) DEFAULT NULL COMMENT ‘实例ID’,
current_load
bigint(20) DEFAULT NULL COMMENT ‘当前负载’,
peak_load
bigint(20) DEFAULT NULL COMMENT ‘峰值负载’,
pool_size
bigint(20) DEFAULT NULL COMMENT ‘线程数’,
active_size
bigint(20) DEFAULT NULL COMMENT ‘活跃线程数’,
queue_capacity
bigint(20) DEFAULT NULL COMMENT ‘队列容量’,
queue_size
bigint(20) DEFAULT NULL COMMENT ‘队列元素’,
queue_remaining_capacity
bigint(20) DEFAULT NULL COMMENT ‘队列剩余容量’,
completed_task_count
bigint(20) DEFAULT NULL COMMENT ‘已完成任务计数’,
reject_count
bigint(20) DEFAULT NULL COMMENT ‘拒绝次数’,
timestamp
bigint(20) DEFAULT NULL COMMENT ‘时间戳’,
gmt_create
datetime DEFAULT NULL COMMENT ‘创建时间’,
gmt_modified
datetime DEFAULT NULL COMMENT ‘修改时间’,
PRIMARY KEY (id
),
KEY idx_group_key
(tp_id
,instance_id
) USING BTREE,
KEY idx_timestamp
(timestamp
) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT=‘历史运行数据表’;
可以看到,建表语句中有三个关键字段:
thread_pool_id:表示当前数据的线程池标识
instance_id:应用可能集群部署,标识集群下唯一的线程池
timestamp:记录线程池运行数据产生时的时间戳
有一个问题,线上的线程池是源源不断产生运行数据的,迟早不得把表的数据量推到上亿?
因为数据是有时效性的,过了一定时间之后,就没有必要再占用实时的资源
针对上述问题提供两种解决方案:
-
假设数据存储 1 天,如果超出这个时间,直接删除即可
-
同上所述,过期数据可以保留到备份表中,并删除 his_run_data 数据
可能有的小伙伴还会担心,数据量太大会不会导致查询时过慢?
我们可以算一下,假设有 100 个应用,每个应用部署 10 个节点
假设数据有效期为 1 小时,那么可以产出的数据是 72 万,一天也就是 1728 万
对于 MySQL 而言,几千万数据量以下针对索引的查询,都不会产生性能瓶颈
04
==
如何定义公共监控?
抽象线程池存储
=======
上面说到,线程池的采集历史运行数据在各个应用系统中,数据的存储、定期删除是否可以抽象出来,避免重复的工作
如果选择抽象数据存储,客户端节点与服务端之间的交互如下:
-
客户端定时采集线程池历史运行数据,将数据打包好发送服务端
-
服务端接收客户端上报的数据,进行数据入库持久化存储
-
服务端定期删除或存档客户端线程池历史运行数据
-
由服务端统一对外提供线程池运行图表的数据展示
这里有个小问题,客户端如何打包发送给服务端?定时采集数据后直接上报是不是可行呢
不推荐采集、上报两种行为放到一个流程中,好的设计应该是要 分离开职责;而且,如果在上报过程中网络出现阻塞等等问题,会耽误采集线程的下一次采集结果
我们可以使用多线程生产、消费模型来做,相信大家初学多线程一定都学过这个设计
// 缓冲队列
private BlockingQueue messageCollectVessel = new ArrayBlockingQueue(bufferSize);
// 生产者
Message message = collector.collectMessage();
boolean offer = messageCollectVessel.offer(message);
if (!offer) {
log.warn(“Buffer data starts stacking data…”);
}
// 消费者
while (true) {
try {
Message message = messageCollectVessel.take();
messageSender.send(message);
} catch (Throwable ex) {
log.error(“Consumption buffer container task failed. Number of buffer container tasks :: {}”, messageCollectVessel.size(), ex);
}
}
创建阻塞缓冲队列,由定时线程池采集历史运行数据,并放到缓冲队列中;然后起一个线程,循环消费即可
极端情况下缓冲队列元素会出现堆积,最新采集的线程池数据也就无法插入成功,为了不影响客户端的运行,仅做异常警告处理
使用最新抽象出来的客户端、服务端交互流程,有以下几个优点
-
数据的存储和查询展示由服务端提供功能,减轻客户端压力和重复工作量
-
历史运行数据的删除或备份操作由服务端统一执行
-
不同的项目不需要为线程池历史运行数据分别创建表结构存储
-
形成交互规范,避免业务发散单独开发,中心化的设计更利于技术的迭代和管理
监控图表展示
======
2021年Java中高级面试必备知识点总结
在这个部分总结了2019年到目前为止Java常见面试问题,取其面试核心编写成这份文档笔记,从中分析面试官的心理,摸清面试官的“套路”,可以说搞定90%以上的Java中高级面试没一点难度。
本节总结的内容涵盖了:消息队列、Redis缓存、分库分表、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。
目录:
(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程)
部分内容:
对于每一个做技术的来说,学习是不能停止的,小编把2019年到目前为止Java的核心知识提炼出来了,无论你现在是处于什么阶段,如你所见,这份文档的内容无论是对于你找面试工作还是提升技术广度深度都是完美的。
不想被后浪淘汰的话,赶紧搞起来吧,高清完整版一共是888页,需要的话可以点赞+关注
止Java的核心知识提炼出来了,无论你现在是处于什么阶段,如你所见,这份文档的内容无论是对于你找面试工作还是提升技术广度深度都是完美的。
不想被后浪淘汰的话,赶紧搞起来吧,高清完整版一共是888页,需要的话可以点赞+关注