1 有界队列的阻塞特点,满元素时 怎么阻塞生产者,空元素时怎么阻塞消费者
特点:
- 当队列满时,生产者线程会被阻塞,直到队列有空位
- 当队列空时,消费者线程会被阻塞,调用直到有新元素加入
队列满时,调用await()方法释放锁并阻塞生产者
消费者消费完一条数据,调用signal() 唤醒一个生产者。
2. aqs为啥使用双向队列 既然双向队列这么好,那还要单向队列干嘛
双向队列查询和修改的是时间复杂度都是o(1),很适合效率要求很高的线程切换场景
但是为啥还要单向队列呢?首先双向队列肯定继承了单向队列的功能,并且需要更多的指针记录前后节点信息,需要耗费更多的内容,并且较复杂,对使用者不友好。只能想到这点区别了。
3. completableFuture的特点
因为future会阻塞主线程 ,知道异步线程返回。而completableFuture很好的解决了这个问题
4.concurrenthashmap为啥不允许空元素 key和value都不允许为空
防止歧义,get(key)时,是元素本身就不存在,还是value是null
待补充 线程安全问题。要仔细理解...
5.怎么排查线上cpu飙高问题
刚好排查过这类问题,巡检时发现机器top超100%,用如下方法定位到具体的报错日志。
发现有一个seg自然语言的二方包,在循环调用内部的oa系统接口,不通。然后日志中也在不断输入报错信息。
这个就等吧,等他们接口回复了,重启下服务就正常了。
这一般是代码问题,日志上肯定是有反应的。所以第一目的还是找日志。但是一台机器上有很多
服务怎么定位具体是哪个呢?
步骤如下 top 查看是哪个进程cpu占用率高。如果持续超过100% 那么必然有问题。
然后pwdx pid 就能看到是哪个服务的问题了(此时进入到这个服务的日志中,可以简单排查)。然后打印堆栈信息,能给直接给出报错的问题代码。
命令如下:先找出进程中占用率高的线程,top -Hp pid。
然后在java中用Integer.toHexString(线程id) 把10进制的线程id转成16进制。
然后再打印这个线程的堆栈信息:jstack pid |grep -A 20 线程di
打印的日志可以直接定位报错的代码行 很好用。
我这里的问题是三方包调用接口不同,然后一直再重复调用。修复接口就行了。不过一般都要再重启服务才能生效。
6.怎么排查oom问题,或者内存占用率高的问题。
首先可以使用free -g 查看机器内存使用率。
再者 如果出现oom 一般系统就崩溃了,属于很严重的问题。这种问题在系统日志中会有体现的。
所以看下崩溃的服务日志,然后属于下面哪种异常
-
Java heap space:堆内存不足Metaspace:元空间溢出(JDK 8+)Direct buffer memory:堆外内存耗尽Unable to create new native thread:线程数超限
如果是堆内存不足,那就是可能存在大对象,比如数据库没有分页查询的数据量太大,年轻代放不下 直接放到老年代了。还有内存泄露(缓存未设置过期时间,就一直存在)
如果是元空间溢出,我们要先思考元空间是放类文件的,还有常量池的内容。那就有可能是动态加载的类太多了。可以考虑检查下代码中的动态代理或者类加载器的相关代码,或者再扩大下元空间的大小。当然了 1.8后的版本去除了元空间,放磁盘了。不会出现这种问题了。
还有其他两个不说了 没见过,
此外还有可以获取dump信息,进一步排查大对象信息。命令如下:
jmap -dump:live,format=b,file=heap.hprof <PID>
但是这种方法不太现实 打印的东西太多太乱,最好能够调用出 jconsole 可以直接查看对象信息。大对象是放到最前面的。
7.G1 垃圾收集的特点,为什么低延迟
这种问题 很复杂很深层次的问题 我们只要记住使用场景就行了。
G1是1.9后默认的垃圾回收,起始于1.74u。低延迟的原因有,多分区,并发处理,标记-整理清除算法,局部复制清除算法,可设置stopworld时间,适合大型服务 大内存场景。
8,幸存一区和幸存二区 来回回收超过15次 就被放到老年代了,为啥是15次。
因为存放这个值的类型是4个bit,也就是最大值也就是1111,8+4+2+1=15,所以不但默认是15,而且最大也只能是15.
9,再说equals
单就对象而言,如果重写 ,那么比较的是值,不重写比较的是地址,等同于==
10,list是1.5倍扩容,map是2倍扩容。
此外 如果已知数据量大小,可创建时就设置集合大小,防止扩容带来的消耗。 list是1.5倍是因为 避免额外空间浪费,map 2倍扩容是因为可很大程度上避免重写计算哈希,并降低冲突。
11,聚集索引 非聚集索引
再次理解这俩个概念,以前理解的都是错的。。。
本质区别,前者索引叶子节点存放实际数据,后者只存指针。所以后者要做回表查询。
再者innodb中,除了主键索引是聚集索引,其他索引都是非聚集索引。所以二者在一个表中是共存的状态。而且通过主键索引查询的速度特别快。
还有就是myisam中都是非聚集索引,因为是表级锁,纯读场景无冲突,所以适合大量的查询操作,并且支持全文索引,倒排索引,内存开销低
12. 不适合建索引的情况
更新多,体量小,散列程度第,函数操作多
14.索引是可以设置长度的,要是必要的,尤其是对文本类型的,长度过程,很占内存。
15.时刻关注阿里开发手册
像包名要小写,用单数,一个单词;接口方法 不显示声明public abstract;枚举类的值要大些;方法命名,查询单个数据用getxxx,查询集合用listxxx,计数用countxxx;实体类用xxxDo于表字段对应,service层用xxxDTO。long型要用 大写L。浮点型数据判断不能用==或equals,要用math或者bigDecimal。养成习惯,在pojo类中都重写toString()方法用于日志打印。Java序列化是用来把对象写到文件中的,当然了也可先用JSON转成字符串再写进去。使用多个锁时,一定要保证锁的顺序性,防止死锁。switch必须要有default,即使啥都不写。要习惯用大于小于判断数字,而不是用等于。优先使用spring的BeanUtils做浅拷贝 效率还行。pattern正则实例化的时候 要放到方法体外,做预编译,提高效率,可作为静态变量或成员变量。业务错误码分单个大类,A00开头表示用户错误,B00开头表示系统错误,C00开头表示第三方错误。http错误码可由500内部异常代为处理。打印日志不要使用json.tojsonstring,而是重写对象的tostring方法。单元测试间保持独立,不要互相调用。数据库小数类型都用decimal.对于过长的大字段建议独立出一张表来关联,避免影响其他字段的索引效率。表数据超过500万行或2g,才考虑分库分表。字段无符号表示字段值>=0,并且舍弃了小数部分,扩大了数据范围。尽量建唯一索引,即使是组合出来的唯一索引,这样不仅是为了提高查询速度,也能避免脏数据。虽然加索引会影响插入速度,但是可以忽略不计。在varchar上建索引必须指点索引长度。严禁使用左模糊查询,可以考虑用反转数据(新字段)+右模糊实现。全文索引具有分词功能 mysql具有此能力。
ALTER TABLE articles ADD FULLTEXT ft_index (content); -- 全文单列索引创建
禁止使用外键,由业务来控制。修改操作,必须要先查询再修改,保持目标数据存在。多表查询必须使用别名,就用t1,t2...表示。mysql utf8编码 一个中文字符占三个字节。
16.禁止使用大POJO类,造成字段yao不要怕麻烦 多建类。
17.业务异常码是补充http状态码不足的问题。
但是http状态码也不能扩展,所以如果内部异常了要体现在http状态码上可以统一返回一个500内部异常,然后在响应体中具体给出详细的自定义业务异常码。
18. java调用三方接口时,接收的数据格式可以都用string字符串接收,再转成json对象,再转成pojo实体类。
19,单例模式 不要用双重检查锁,慢而且有延迟风险,可以用volatile关键字修饰创建的对象变量,然后加一层锁就行,并且再锁内部再判一次null.
20,tps qps 并发
tps:一秒钟处理的完整事务数,可能包含多个接口。根据统计的角度不同而有所变化,可能是加载一个页面的所有接口算作一个tps,也可能是一个业务流程。
qps:一秒钟处理一个接口的次数,所以一个tps可能包含多个qps.
21, (22有修正)项目的tps, 是这样的 我的项目中计算 tps的结果是100。没有使用jmeter这样的工具,而是使用批量数据处理,得出的结果。计算的业务是银行的贷款审批流程。需要处理批量业务,要求半小时内处理完,总数据量大概20万。处理单个业务大概需要1s,最大支持40个并发,3台机器。所以40*3*60*30=216000 刚好能处理完20万业务请求.
其实最大能限制处理性能的是数据库的处理能力,并不是有多个机器,多大的并发。因为这里是流程较长 需要处理调用的外部接口多,瓶颈不在数据库,可以单独的累加。
22,怎么理解并发?(修正实际业务系统处理能力数据)
并发数是指同时处理的请求数。个人感觉是可以理解成机器能处理的线程数,即逻辑线程数。
(1)那么,qps=并发/0.5=64=逻辑线程数/0.5 因为是一个接口也就是tps=qps.
如果我们机器的逻辑线程数32,从日志中得出的贷款审批流程需要0.5秒。另外压测得出20万条数据在10分钟内处理完,有3台机器。
(2)从而反向推理出200000/3/10/60=qps=tps=100(单个机器的处理能力)
可以看出1,2得出的结果不一样,实际的处理能力更强,也就是并发更高,并发=55.比逻辑线程数要大。
这是因为 系统是io密集型的。 所以单台机器的并发是55,tps(吞吐量)=100, 平均用时0.5s
所以由于线程的切换,程序的并发能力是变换的,并发数也就不等于线程数。
总之要根据压测结果去推并发数,然后再调整程序中的线程池,内存去优化结果。不能根据tomcat的并发数、线程数 ,连接数(10000)来判断。 要实事求是。
23,redis缓存和mysql数据一致性问题
先删除缓存再更新数据库,就可以了。但是极端情况下,由于这俩操作不在一个事务中,所以还是要做最终一致性处理,就是把更新失败的操纵记录到队列中,重试就行。
24,为什么java使用lua脚本操作redis 因为保证了查询和删除在一个事务中。
25,springboot 自动装配
这也是spring 与springboot之间的最大的区别。
所谓自动装配,是声明了@EnableAutoConfiguration 后 项目启动后采用spi机制。会加载jar包中meta-info目录下下的spring.factories文件,读取其中的bean类,装载到容器中。
26 ,redis哨兵和redis cluster集群的区别
首先两个都是多节点 主从结构,并且支持故障自动转移,读写分离。 哨兵就是做监听和故障转移的。但是redis cluster支持多主多从和自动扩容,不过也太麻烦了

被折叠的 条评论
为什么被折叠?



