面试笔试题目整理
〇、热点问题
1、春晚红包提现流程-----如果保证高并发可用?
高可用HA衡量,可用性=平均故障间隔/(平均故障间隔 + 故障恢复平均时间),9的位数越多,可用性越强
根据CPU和内存使用限制定义容器的伸缩能力(使用率超过阈值,扩充一个容器);
简单路由服务,实现负载均衡
对容器进行管理和监控,,超过阈值进行告警
设置多台nginx,利用linux的keeplived字段进行探测可用性,当一台Nginx挂了之后,责会自动转移到另一台Nginx机器上来,从而保证高可用。
反向代理时,nginx能够自动探测后端服务的可用性
使用redis主从同步方案来达到高可用,redis主从同步加上sentine哨兵机制来自动探活redis实例。
使用双写数据库,主数据库宕机才往从服务器上写
将读库部署多台,例如部署2台,通过代码段增加连接池组件进行路由读库和探活
延申思路:超时机制、降级(将不影响本次业务的流程也砍掉)、限流(把瞬间不能处理的流量给截断,直接返回给用户,用户可以待会儿再试)
2、几千万的数据如何读取处理?
使用B+数存储数据吧,内存里只需存储索引。
优化分页查询
MySQL作为主库,Redis作为高速数据查询从库的异构读写分离
3、线程池做多服务的登录/redis怎么做分布式缓存?
将各个服务之间需要共享的数据,保存到一个公共的地方(主流方案就是 Redis)
nginx配置反向代理,同时可以开启缓存,就可以从其他服务中拿到缓存的值。
当所有 Tomcat 需要往 Session 中写数据时,都往 Redis 中写,当所有 Tomcat 需要读数据时,都从 Redis 中读。这样,不同的服务就可以使用相同的 Session 数据了。
简化的方案就是使用 Spring Session 来实现这一功能,Spring Session 就是使用 Spring 中的代理过滤器,将所有的 Session 操作拦截下来,自动的将数据 同步到 Redis 中,或者自动的从 Redis 中读取数据。
4、Redis实现分布式锁
使用 SETNX 命令实现分布式锁。一般使用 setnx(set if not exists) 指令,只允许被一个程序占有,使用完调用 del 释放锁。
5、项目的架构设计,不足,如何改进
学成在线是一个B2B2C的教学网站,类似于腾讯课堂,主要功能包括系统管理(CMS)、社交管理、学习中心、教学管理以及门户。采用前后端分离架构进行开发,由用户层、UI层、微服务层、数据层等部分组成。针对不同的管理模块,分别写一个小系统,各子系统可以独立部署并提供服务。前端可根据不同用户提供不同界面。每个模块独立部署,根据模块压力扩充部署的节点。通过CDN实现内容分发,使用户访问离自己最近的服务器,通过Nginx实现负载均衡,将用户请求平均分摊到每个节点上。
主要分为UI层、微服务层、数据层、DevOps
我主要做了CMS内容管理模块,该模块实现了页面和站点的管理。
页面管理:更改页面某一块内容,管理流程如下:
创建站点
创建模板
创建页面(页面标题、访问的URL)
页面预览
页面发布
7、实现负载均衡的方式
8、单机情况和分布式情况不同场景如何设计一些业务,基本现场给场景,让你提供设计思路。
设计思路:将请求拦截在系统上游,降低下游压力。在一个并发量大,实际需求小的系统中,应当尽量在前端拦截无效流量,降低下游服务器和数据库的压力,不然很可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时。
限流:前端直接限流,允许少部分流量流向后端。
削峰:瞬时大流量峰值容易压垮系统,解决这个问题是重中之重。常用的消峰方法有异步处理、缓存和消息中间件等技术。
异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。
内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。
消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。
可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了,像淘宝、京东等双十一活动时会临时增加大量机器应对交易高峰。
前端优化
1、前端静态资源缓存,页面静态化和使用cdn缓存或redis缓存
2、限流:1使用验证码防止机器人爬虫脚本自动提交2禁止重复提交,用户提交后按钮置灰
后端优化
1、利用负载均衡,使用多个机器处理并发请求
2、秒杀开始前,前台不能得到秒杀地址,防止提前得到秒杀地址,模拟秒杀请求
3、限制同一个用户id访问频率
4、限制同一时间请求次数,达到请求上限时,随机拒绝部分请求来保证服务可用
5、业务分离,将秒杀系统和其它业务分离,单独放在高配机器上,防止影响其它业务系统
6、将秒杀请求放入到消息队列队列,后台订阅消息减库存,检测消息队列长度,达到最大库存不加消息队列,直接返回秒杀失败的消息
7、利用缓存应对读请求,利用缓存减轻数据库压力
8、利用缓存应对写请求,将数据库的库存数据转到redis里面,所有减库存操作都在redis里面进行,然后通过后台进程把redis里面的用户秒杀请求同步到数据库
9、Java对象头有哪些信息
mark word表示有无锁,垃圾回收标志,分代回收的年龄,偏向锁,二进制
10、ThreadLocal用过吗,可能会出现什么问题,如何解决
Entry继承自WeakReference(弱引用,生命周期只能存活到下次GC前),但只有Key是弱引用类型的,Value并非弱引用。(问题马上就来了)
由于ThreadLocalMap的key是弱引用,而Value是强引用。这就导致了一个问题,ThreadLocal在没有外部对象强引用时,发生GC时弱引用Key会被回收,而Value不会回收。
当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object>的键值对,造成内存泄露。
解决:
在调用ThreadLocal的get()、set()方法时完成后再调用remove方法,将Entry节点和Map的引用关系移除,这样整个Entry对象在GC Roots分析后就变成不可达了,下次GC的时候就可以被回收
或者ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。
个人学习方式、性格特点
一、面经内容
(一)JVM、GC、多线程
1、线程间通信的方式
使用 volatile 关键字、使用Object类的wait() 和 notify() 方法、使用JUC工具类 CountDownLatch、使用 ReentrantLock 结合 Condition
管道、信号量、消息队列、共享内存 、套接字
2、JVM内存模型。
元空间并不在虚拟机中,而是使用本地内存。
3、JUC包。
包括atomic包、locks包(可重入锁、读写锁)、线程安全的集合、Executor接口及线程池
4、OOM内存溢出/泄露:
内存溢出:out of memory,内存不够
内存泄漏:Memory Leak ,指程序在申请内存后,无法释放已申请的内存空间。分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费(static、内部类、各种连接、集合修改后无法remove)
内存泄漏会导致内存溢出。
5、类加载机制
类加载器:启动、扩展extension、应用程序application classloader
相同class文件,在不同类加载器中,产生的类也不相同
6、class文件加载到jvm中的一个流程是什么?(类加载机制)
准备:static变量,解析:常量池转为直接引用
7、并发大的情况下,核心线程池该如何设置参数?大流量进来会不会堵塞整个流程(通过扩容服务器的方式?)
参数:核心池大小、最大池大小、任务队列、线程活动时间、线程活动时间的单位
阻塞队列可选数组的、链表的、不存数据的、优先级的、
饱和策略:队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是 AbortPolicy,表示无法处理新任务时抛出异常。
常见策略:AbortPolicy直接抛出、CallerRunsPolicy:只用调用者所在线程来运行任务、DiscardOldestPolicy:丢弃队列里最近的一个任务、DiscardPolicy:不处理,丢弃掉
8、乐观锁,悲观锁,AQS以及volatile,还有synchronized
悲观锁:synchronized 和 ReentrantLock 等独占锁就是悲观锁
乐观锁:使用版本号机制和 CAS 算法实现,适用于多读。java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。
AQS同步器:locks.AbstractQueuedSynchronizer,用来构建锁和同步器的框架,如Semaphore、可重入读写锁都是基于AQS构建的。
AQS原理:请求资源空闲,设为有效进程并加锁;不空闲,加入CLH队列,即将暂时获取不到锁的线程加入到队列中。
AQS 使用一个 int 成员变量 (state) 来表示同步状态,使用 CAS 对该同步状态进行原子操作实现对其值的修改。
CLD队列:虚拟的双向队列,只存在节点之间的关系,将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。
volatile:保证线程对该变量操作的内存可见性, 禁止指令重排序。
synchronized:解决的是多个线程之间访问资源的同步性,synchronized 关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。可以通过双重校验锁(volatile和synchronized对类对象加锁)实现单例模式。
9、春晚红包提现流程-----如果保证高并发可用?
高可用HA衡量,可用性=平均故障间隔/(平均故障间隔 + 故障恢复平均时间),9的位数越多,可用性越强
根据CPU和内存使用限制定义容器的伸缩能力(使用率超过阈值,扩充一个容器);
简单路由服务,实现负载均衡
对容器进行管理和监控
设置多台nginx,利用linux的keeplived字段进行探测可用性,当一台Nginx挂了之后,责会自动转移到另一台Nginx机器上来,从而保证高可用。
反向代理时,nginx能够自动探测后端服务的可用性
使用redis主从同步方案来达到高可用,redis主从同步加上sentine哨兵机制来自动探活redis实例。
使用双写数据库,主数据库宕机才往从服务器上写
将读库部署多台,例如部署2台,通过代码段增加连接池组件进行路由读库和探活
延申思路:超时机制、降级(将不影响本次业务的流程也砍掉)、限流(把瞬间不能处理的流量给截断,直接返回给用户,用户可以待会儿再试)
10、年轻代GC用什么算法,老年代用什么算法
新生代采用复制(对象满就扫描),老年代采用标记-整理算法(存活的向左移动)
11、线程数量怎么设定,考虑哪些方面?
判断线程是计算密集型(等于或略微大于CPU核数)还是IO密集型
根据CPU使用率和CPU负载判断是否合理
根据任务使用率设置队列的大小
考虑下游系统的抗并发能力。
12、上下文切换消耗cpu时间如何优化
单CPU进程上下文,尽量切换次数少些(增大时间片长度)
进程绑定到特定的CPU核里,那么就不会涉及到跨CPU切换
13、了解并发编程的知识吗?
并发是指两个或多个事件在同一时间间隔发生
JUC下包含许多并发容器:ConcurrentHashMap/CopyOnWriteArrayList/ConcurrentLinkedQueue/BlockingQueue阻塞队列/ConcurrentSkipListMap跳表
14、ThreadLocal变量,threadLocal是做什么的,说一下
为每一个使用这个变量的线程都保存有一份独立的副本,提供get/set方法
应用场景:
Tomcat。每次访问都是一个新的线程,每一个线程都独享一个 ThreadLocal
ios&安卓下载,传递的设备信息,返回给不同的客户端。
15、为什么不能用Excutor创建线程?造成资源耗尽
创建线程池的方式:ThreadPoolExecutor的构造(可以包含阻塞队列和threadFactory)、 Executor 框架的工具类 Executors
原因:
FixedThreadPool、SingleThreadPool线程池使用链表实现的阻塞队列,不设大小理论上队列容量无上限,所以可能会堆积大量请求从而导致OOM
CachedThreadPool允许创建线程数量为Integer.MAX_VALUE可能会创建大量线程从而导致OOM
16、讲一下Full GC
回收新生代和老年代:老年代空间不足,空间担保失败,建议调用system.gc()
17、频繁的full gc有什么问题?怎么排查?
系统性能变低。
一般full gc不会频繁执行,如果出现了就要去查看一下是不是创建了大的对象或者空间分配担保失败。调大参数。pringtGCDetail
18、sychronized修饰普通方法和静态方法,锁的对象有什么不同
Synchronized修饰静态方法,实际上是对该类对象加锁,俗称“类锁”。
Synchronized修饰非静态方法,实际上是对调用该方法的对象加锁,俗称“对象锁”。
两个对象访问非静态,不会产生互斥。
19、线程池的创建方式,7大参数、阻塞队列、拒绝策略、大小如何设置
ThreadPoolExecutor
线程池的阻塞队列--工作队列
饱和策略---拒绝策略
常见阻塞队列:数组的、链表的、不存数据的、优先级的、
常见饱和策略:抛出、丢弃、不处理
20、锁机制:乐观锁CAS、悲观锁synchronized和ReentrantLock、实现原理以及区别
ReentrantLock 是类,那么它就提供了比 synchronized 更多更灵活的特性:等待可中断、可实现公平锁、可实现选择性通知(锁可以绑定多个条件)、性能已不是选择标准。
synchronized 依赖于 JVM 而 ReenTrantLock 依赖于 API
JDK1.6 对synchronized 引入了大量的优化,如偏向锁(偏向于第一个获得它的线程)、轻量级锁(不申请互斥量)、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。
21、Java内存模型和volatile的原理,以及volatile和synchronized区别
volatile:从主存中读取,只能保证可见性,不能保证原子性
synchronized:只有当前线程可以访问该变量,其他线程被阻塞住;可以被编译器优化
22、ThreadLocal线程本地存储原理,源码层面
value不能被回收,只能手动remove
23、AQS同步队列器原理,CLH队列
构建各种同步队列器,比如:ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask
将暂时获取不到锁的线程加入到CLH队列中
对资源的模式包括共享模式(多个线程可同时执行,如:CountDownLatch、Semaphore、CountDownLatch、 CyclicBarrier、ReadWriteLock、ReentrantReadWriteLock)和独占模式(只有一个线程能执行,如:ReentrantLock,又可分为公平锁和非公平锁)。
24、AQS组件:ReentrantReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore原理掌握
CountDownLatch 是计数器
CyclicBarrier是循环栅栏
ReentrantReadWriteLock共享锁,ReentrantLock排他锁;可以从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。
Semaphore (信号量)可以指定多个线程同时访问某个资源。
synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore (信号量)可以指定多个线程同时访问某个资源,Semaphore 只是维持了一个可获得许可证的数量。Semaphore 经常用于限制获取某种资源的线程数量。使用 acquire方法(阻塞)使用 tryAcquire 方法,该方法如果获取不到许可就立即返回 false。
25、JUC原子类
包括atomic包、locks包(可重入锁、读写锁)、线程安全的集合、Executor接口及线程池
26、集合框架的多线程实现类:CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、ArrayBlockingQueue、LinkedBlockingQueue、ConcurrentLinkedQueue、ConcurrentLinkedDeque
27、详细介绍一下 CMS 垃圾回收器?
CMS 使用的是标记-清除的算法实现的,所以在 GC 的时候会产生大量的内存碎片
在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。
28、介绍一下常见的垃圾回收器
Serial 单线程收集器、 Serial Old 收集器(标记-整理算法,老年代),ParNew 收集器(停止-复制算法,新生代,多线程),Parallel Scavenge 收集器(停止-复制算法,高吞吐量的并行收集器),高吞吐量的Parallel Old 收集器(停止-复制算法),高并发的 CMS(Concurrent Mark Sweep)收集器(标记-清除算法),基于优先列表的G1 收集器
(二)MySQL、MongoDB、Redis、Nginx
1、事务的特性和隔离级别
未提交读、提交读、可重复读、串行化
脏读、幻读、不可重复读、丢失修改
2、MySQL引擎。
事务、聚合、外键、锁粒度
3、Innodb怎么保证事务的特性。
行级锁
4、索引的数据结构
InnoDB 存储引擎的默认索引实现为 B+ 树索引
5、Redis的底层数据结构
6、redis过期删除&内存淘汰
同时使用了惰性过期和定期过期两种过期策略。
7、缓存雪崩&缓存穿透是什么,如何解决
8、布隆过滤器怎么实现的
9、redis几种结构介绍+实现原理、string最大存多少数据
10、redis中数字怎么存储
11、redis持久化的方式
12、Redis缓存淘汰策略,LRU和LFU的特点、区别
13、主从复制、主从一致、分库分表
14、缓存和数据库双写时的数据一致性
15、为什么单线程还那么快?(非阻塞多路IO复用)☆
16、redis集群、哨兵机制
对 Redis 实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个 Redis 系统的可用性。
17、分布式锁★
多线程资源竞争时,使用SETNX命令实现分布式锁
使用SETNX命令获取锁,若返回0(key已存在,锁已存在)则获取失败,反之获取成功
18、netty的零拷贝机制★
19、nginx怎么做反向代理及负载均衡,以及常用的算法
算法:轮询、weight、ip的hash(可以解决session的问题)、fair(响应时间短的优先分配)、url_hash(第三方)
http { upstream MyServer { # 定义负载设备ip及其设备状态-----上游服务配置服务器 server 10.10.10.11:80; # 做负载均衡的服务器地址B server 10.10.10.12:80; # 做负载均衡的服务器地址C, } server { listen 80; location / { proxy_pass http://MyServer; # 指向上面设置反向代理转发的服务器---反向代理到上游服务器 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
(三)Spring相关框架
(四)ElasticSearch、Lucene
(五)协同过滤、推荐系统、用户画像
(六)算法
1、口述算法:统计字符串里每个字符出现的次数。
2、生产者消费者模型,BlockingQueue
3、单例模式
synchronized双重校验锁(volatile和synchronized对类对象加锁)
4、动规和贪心有什么相似的地方以及区别
5、共享屏幕写个多线程
6、写一段代码,实现三个线程,第一个线程打印1,2,3,4,第二个线程打印5,6,7,8,第三个线程打印9,10,11,12,
然后第一个线程再去打印13,14,15,16,一直打印下去。
7、手写观察者模式
(七)Java基础、设计模式
1、JDK用到了什么设计模式
2、泛型原理
类型擦除、限定通配符和非限定通配符
3、StringBuilder的append方法和“+”有什么区别,底层实现
4、equals和==的区别:一般重写equals,就还要重写hashcode。HashCode相等,equals不一定相同,但是如果equals相同,hashcode一定相同。
5、为什么重写equals还要重写hashcode,不重写会怎样?
6、public protected default private的作用范围
7、在一个文本中检索某个字符串可以怎么做?
正则表达式
(八)408
1、socket是属于什么层的
2、平衡二叉树和红黑树的区别
4、tcp和udp的区别
5、udp不面向连接数据怎么传输的呢
6、基于tcp和udp的应用层有哪些(比如http)
7、讲一下cookie和session的区别和作用
8、什么是http无状态连接
9、sessionID是在哪里生成的
10、介绍红黑树的原理和应用★
11、SSL连接的过程、对称加密、非对称加密
(九)场景题
1、场景题,几千万的数据如何读取处理?
使用B+数存储数据吧,内存里只需存储索引。
优化分页查询
2、场景题:请求远程服务器,走的缓存的场景
3、场景题:用线程池做登录校验,会有什么问题
二、项目相关
1、页面静态化
2、微服务
3、前后端分离
4、负载均衡
5、路由访问
6、限流
7、高并发
8、其他功能
三、其他内容
1、读了哪些书
Java核心技术、redis实战、spring手写代码
2、怎么学Java
看视频、书、直播课
3、以后规划
4、实习的内容
5、说几个你最近在看的技术(MySQL,多线程)
6、把你知道的中间件都说一下
包括atomic包、locks包(可重入锁、读写锁)、线程安全的集合、Executor接口及线程池
本文来自博客园,作者:哥们要飞,转载请注明原文链接:https://www.cnblogs.com/liujinhui/p/15153910.html