一、计算机基础
1.1、数据结构
1、树
eg:判断完全二叉树、满二叉树
树的深度、宽度遍历
最近公共祖先
字典树(trie tree)
2、栈
O(1)实现push、pop操作获取最小值
实现队列
3、bitmap
1.2、网络
1、HTTP是网络七层协议中哪一层的协议?
参考答案:应用层。
解题思路:基础知识
考察点:OSI七层协议的基础知识
分类:HTTP,基础知识,网络,{校招,社招都可以用}
难度分级:P4
2、Get和Post区别?
参考答案:
HTTP定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET、POST、PUT、DELETE。这里主要讨论前2种:GET和POST。
GET - 从指定的资源请求数据。
POST - 向指定的资源提交要被处理的数据。
在开始对比前,需要理解1个概念,幂等(idempotent)
在HTTP/1.1规范中幂等性的定义是:Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.
幂等的意味着对同一URL的多个请求应该返回同样的结果。根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的。所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。
| GET | POST |
后退按钮/刷新 | 无害 | 数据会被重新提交(浏览器应该告知用户数据会被重新提交) |
书签 | 可收藏为书签 | 不可收藏为书签 |
缓存 | 能被缓存 | 不能缓存 |
编码类型 | application/x-www-form-urlencoded | application/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。 |
历史 | 参数保留在浏览器历史中。 | 参数不会保存在浏览器历史中。 |
对数据长度的限制 | 是的。当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL的最大长度是 2048 个字符)。 | 无限制 |
对数据类型的限制 | 只允许 ASCII 字符。 | 没有限制。也允许二进制数据。 |
安全性 | 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。 在发送密码或其他敏感信息时绝不要使用 GET ! | POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。 |
可见性 | 数据在 URL 中对所有人都是可见的。 | 数据不会显示在 URL 中。 |
解题思路:HTTP基础知识,从面向数据提交方式、安全性等方面进行对比,如果能阐述幂等性更好
考察点:幂等性,GET和POST信息的提交方式
分类:网络应用层,HTTP基础知识{校招,社招都可以用}
难度分级:P4,P5
3、描述一下一次HTTP请求从请求到返回的过程,越详细越深入越好。(或者,访问一个url 都经历过了哪些事情,越详细越好)
1)把URL分割成几个部分:协议、网络地址、资源路径。其中网络地址指示该连接网络上哪一台计算机,可以是域名或者IP地址,可以包括端口号;协议是从该计算机获取资源的方式,常见的是HTTP、FTP,不同协议有不同的通讯内容格式;资源路径指示从服务器上获取哪一项资源。例如:http://www.guokr.com/question/554991/
协议部分:http
资源路径:/question/554991/
2)如果地址不是一个IP地址,通过DNS(域名系统)将该地址解析成IP地址。IP地址对应着网络上一台计算机,DNS服务器本身也有IP,网络设置包含DNS服务器的IP。
例如:www.guokr.com 不是一个IP,向DNS询问请求www.guokr.com 对应的IP,获得IP:111.13.57.142。这个过程里,电脑直接询问的DNS服务器可能没有www.guokr.com对应的IP,就会向它的上级服务器询问,上级服务器同样可能没有,就依此一层层向上找,最高可达根节点,找到或者全部找不到为止。(DNS缓存和解析过程是一个考察点,有些面试者能叙述出完整的过程,有些只能给出笼统的结果,以下是DNS缓存策略)
浏览器缓存 – 浏览器会缓存DNS记录一段时间。有趣的是,操作系统没有告诉浏览器储存DNS记录的时间,这样不同浏览器会储存个自固定的一个时间(2分钟到30分钟不等)。
系统缓存 – 如果在浏览器缓存里没有找到需要的记录,浏览器会做一个系统调用(windows里是gethostbyname)。这样便可获得系统缓存中的记录。路由器缓存 – 接着,前面的查询请求发向路由器,它一般会有自己的DNS缓存。
ISP DNS 缓存 – 接下来要check的就是ISP缓存DNS的服务器。在这一般都能找到相应的缓存记录。
递归搜索 – ISP的DNS服务器从跟域名服务器开始进行递归搜索,从.com顶级域名服务器到Facebook的域名服务器。一般DNS服务器的缓存中会有.com 域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了。
3)如果地址不包含端口号,根据协议的默认端口号确定一个。
端口号之于计算机就像窗口号之于银行,一家银行有多个窗口,每个窗口都有个号码,不同窗口可以负责不同的服务。端口只是一个逻辑概念,和计算机硬件没有关系。
例如:www.guokr.com 不包含端口号,http协议默认端口号是80。
如果输入的url是http://www.guokr.com:8080/,那表示不使用默认的端口号,而使用指定的端口号8080。
4)向2和3确定的IP和端口号发起网络连接。例如:向111.13.57.142的80端口发起连接
5)根据http协议要求,组织一个请求的数据包,里面包含大量请求信息,包括请求的资源路径、你的身份。例如:用自然语言来表达这个数据包,大概就是:请求 /question/554991/,我的身份是xxxxxxx。
6)服务器响应请求,将数据返回给浏览器。数据可能是根据HTML协议组织的网页,里面包含页面的布局、文字。数据也可能是图片、脚本程序等。现在可以用浏览器的“查看源代码”功能,感受一下服务器返回的是什么东东。如果资源路径指示的资源不存在,服务器就会返回著名的404错误。
7)如果(6)返回的是一个页面,根据页面里一些外链的URL,例如图片的地址,按照(1)-(6)再次获取。
8)开始根据资源的类型,将资源组织成屏幕上显示的图像,这个过程叫渲染,网页渲染是浏览器最复杂、最核心的功能。
9)将渲染好的页面图像显示出来,并开始响应用户的操作。
以上只是最基本的步骤,实际不可能就这么简单,一些可选的步骤例如网页缓存、连接池、加载策略、加密解密、代理中转等等都没有提及。
即使基本步骤本身也有很复杂的子步骤,TCP/IP、DNS、HTTP、HTML:每一个都可以展开成庞大的课题,而浏览器的基础——操作系统、编译器、硬件等更是一个比一个复杂。
这道题说难不难,说容易不容易,不同层次不同水平的面试者可能会有不同的表现和见解,上面仅是答题要点,面试人员的表现和理解层次需要面试官酌情判定。
解题思路:从一个请求的时间上顺序进行阐述,涉及多个过程,最好能做深入
考察点:网络综合知识
分类:基础知识,网络{校招,社招都可以用}
难度分级:P4,P5,P6
4、举出6个http的状态码,502 和 504有什么区别。
状态码范围 | 作用 |
100-199 | 用于指定客户端应相应的某些动作。 |
200-299 | 用于表示请求成功。 |
300-399 | 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。 |
400-499 | 用于指出客户端的错误。 |
500-599 | 用于支持服务器错误。 |
| TCP | UDP |
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
应用场合 | 传输大量数据 | 少量数据 |
速度 | 慢 | 快 |
具体每个码的含义见右边的链接→HTTP状态码大全
502 Bad Gateway:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
504 Gateway Time-out:作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。
502 Bad Gateway:Tomcat没有启动起来
504 Gateway Time-out:Nginx报出来的错误,一般是Nginx做为反向代理服务器的时候,所连接的应用服务器譬如Tomcat无相应导致的
解题思路:基础知识
考察点:HTTP的基础知识,要求能记住工作中常见的几个错误码
分类:网络应用层,HTTP,基础知识{社招}
难度分级:P5
5、UDP,TCP是网络七层协议中哪一层的协议,区别是什么,分别适用于什么场景?
传输层。在TCP/IP模型中,传输层的功能是使源端主机和目标端主机上的对等实体可以进行会话。
在传输层定义了两种服务质量不同的协议。即:传输控制协议TCP(transmission control protocol)和用户数据报协议UDP(user datagram protocol)。
TCP协议是一个面向连接的、可靠的协议。它将一台主机发出的字节流无差错地发往互联网上的其他主机。需要三次握手建立连接,才能进行数据传输。在发送端,它负责把上层传送下来的字节流分成报文段并传递给下层。在接收端,它负责把收到的报文进行重组后递交给上层。TCP协议还要处理端到端的流量控制,以避免缓慢接收的接收方没有足够的缓冲区接收发送方发送的大量数据。
UDP协议是一个不可靠的、无连接协议,不与对方建立连接,而是直接就把数据包发送过去。主要适用于不需要对报文进行排序和流量控制的场合。UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。
解题思路:基础知识,从面向连接、可靠性、应用场合上进行分析对比
考察点:七层协议中传输层的基础知识,着重从TCP vs UDP的区别上考察
分类:七层协议,基础知识,网络,{校招,社招都可以用}
难度分级:P4,P5
6、互联网服务的默认端口是多少?
HTTP 80 SMTP 25 POP3 110 FTP 21 20 TELNET 23
考察点:网络协议,端口
分类:网络应用层,基础知识{校招,社招都可以用}
难度分级:P5
7、tcp的三次握手,如何用Java实现tcp的广播,如果发现网段内的所有集群节点?
1.3、操作系统
1.4、Linux
1、Linux Top有哪些重要参数值得关注,这些参数有什么具体含义?
Linux的top命令详解见http://www.2cto.com/os/201209/157960.html
解题思路:不同水平的面试人员有不同层次的理解,面试官应视其回答情况酌情给分
考察点:Linux运行情况的查看、top命令的使用熟练程度
分类:Linux基本操作{校招,社招都可以用}
难度分级:P4
2、Linux上用什么命令看在线日志?
Linux日志都以明文形式存储,所以用户不需要特殊的工具就可以搜索和阅读它们。还可以编写脚本,来扫描这些日志,并基于它们的内容去自动执行某些功能。
Linux日志存储在 /var/log 目录中。这里有几个由系统维护的日志文件,但其他服务和程序也可能会把它们的日志放在这里。大多数日志只有root账户才可以读,不过修改文件的访问权限就可以让其他人可读。
可以用cat、head、tail等查看,用grep搜索过滤,用cut取字段(列数据),更高级的可以用awk和sed重排日志。
解题思路:日志文件的格式多是文本文件,从文本文件的处理方式的考虑
考察点:日志的存储,Linux查看、搜索文本文件的方式,坑在直接用VI看,如果稍有经验的人这么做,那么基本可以送走了
分类:Linux基本操作{校招,社招都可以用}
难度分级:P4
3、如何输出时间到time.txt文件?
date > time.txt
解题思路:重定向的应用
考察点:Linux命令输出重定向、简单的Linux命令
分类:Linux基本操作{校招,社招都可以用}
难度分级:P4
4、有一个文本文件source.txt,每行有个字符串,请使用shell命令查找包含“beijing”的行,并将结果保存到文件result.txt中
grep -i beijing source.txt > result.txt
解题思路:用grep来对文本文件搜索,注意-i选项用来忽略大小写
考察点:Linux命令输出重定向、grep简单应用
分类:Linux基本操作{校招,社招都可以用}
难度分级:P4
5、查看当前TCP/IP连接的状态和对应的个数?
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
解题思路:用netstat来查看连接和端口,用awk进行统计
考察点: netstat和awk的应用
分类:Linux组合操作{校招,社招都可以用}
难度分级:P5
6、分析apache访问日志,找出访问页面的次数排名前100的ip?
awk '{print $1}' localhost_access_log.txt | sort | uniq -c | sort -n -k 1 -r | head -n 100
解题思路:用awk或者cut来取文本中的第一列,再用sort排序,uniq去重,数字排序,最后输出前100名
考察点:awk、sort、head等文本处理工具组合的应用
分类:Linux组合操作{社招}
难度分级:P5
7、符号链接(symbolic link)和硬链接的区别?
参考答案:
硬链接(hard links):为文件创建了额外的条目。使用时,与文件没有区别;删除时,只会删除链接,不会删除文件;
硬链接的局限性:
1.不能引用自身文件系统以外的文件,即不能引用其他分区的文件;
2.无法引用目录;
操作:ln file link,只能link文件;
符号链接(symbolic links):克服硬链接的局限性,类似于快捷方式,使用与硬链接相同。如果先删除文件,则会成为坏链接(broken),ls会以不同颜色(Ubuntu,红色)显示;操作:ln -s item link,可以link文件和目录;
解题思路:hard link和soft link的区别,结合i-node的知识
考察点:i-node,文件链接
分类:Linux文件系统基本概念{校招,社招}
难度分级:P4
1.5、web
1.6、安全
1.7、设计模式
1、单例模式
注意双重检查,考察synchronized
2、工程模式
3、策略模式
1.8、数据库
1、InnoDB默认什么隔离级别,为什么选用这种级别。选其他的可以么?
参考答案:http://www.cnblogs.com/vinchen/archive/2012/11/19/2777919.html
解题思路:数据库事务
考察点:数据库事务
分类:数据库事务{校招,社招}
难度分级:P4
2、RR级别下,都有哪些锁?有遇到过死锁么,什么情况下发生的死锁?
参考答案:http://hedengcheng.com/?p=771
解题思路:数据库事务
考察点:数据库事务
分类:数据库事务{校招,社招}
难度分级:P4
3、数据服务端的一些命令(show processlist等等)
a. show tables或show tables from database_name; -- 显示当前数据库中所有表的名称
b. show databases; -- 显示MySQL中所有数据库的名称
c. show columns from table_name from database_name; 或show columns from database_name.table_name; -- 显示表中列名称
d. show grants for user_name; -- 显示一个用户的权限,显示结果类似于grant命令
e. show index from table_name; -- 显示表的索引
f. show status; -- 显示一些系统特定资源的信息,例如,正在运行的线程数量
g. show variables; -- 显示系统变量的名称和值
h. show processlist; -- 显示系统中正在运行的所有进程,也就是当前正在执行的查询。大多数用户可以查看自己的进程,但是如果拥有process权限,就可以查看所有人的进程,包括密码。
i. show table status; -- 显示当前使用或者指定的database中的每个表的信息。信息包括表类型和表的最新更新时间
j. show privileges; -- 显示服务器所支持的不同权限
k. show create database database_name; -- 显示create database语句是否能够创建指定的数据库
l. show create table table_name; -- 显示create database语句是否能够创建指定的数据库
m. show engies; -- 显示安装以后可用的存储引擎和默认引擎。
n. show innodb status; -- 显示innoDB存储引擎的状态
o. show logs; -- 显示BDB存储引擎的日志
p. show warnings; -- 显示最后一个执行的语句所产生的错误、警告和通知
q. show errors; -- 只显示最后一个执行语句所产生的错误
r. show [storage] engines; --显示安装后的可用存储引擎和默认引擎
s. show procedure status --显示数据库中所有存储的存储过程基本信息,包括所属数据库,存储过程名称,创建时间等
t. show create procedure sp_name --显示某一个存储过程的详细信息
解题思路:
考察点:MySQL
分类:数据库,硬技能{校招,社招}
难度分级:P4、P5
4、为什么MySQL的索引要使用B+树而不是其它树形结构?为什么不用B树
为什么不用B树:因为B树的所有节点都是包含键和值的,这就导致了每个几点可以存储的内容就变少了,出度就少了,树的高度会增高,查询的时候磁盘I/O会增多,影响性能。由于B+Tree内节点去掉了data域,因此可以拥有更大的出度,拥有更好的性能。
解题思路:MySQL索引数据结构
考察点: MySQL索引数据结构,B+树 B-树
分类:MySQL、数据结构{校招,社招}
难度分级:P4,P5
5、为什么InnoDB中表的主键最好要自增?
InnoDB使用聚集索引,数据记录本身被存于主索引(一颗B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(Inno DB默认为15/16),则开辟一个新的页(节点)。如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置, 当一页写满,就会自动开辟一个新的页。这样就会形成一个紧凑的索引结构,近似顺序填满。由于每次插入时也不需要移动已有数据,因此效率很高,也不会增加很多开销在维护索引上。
如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置,此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。
因此,只要可以,请尽量在InnoDB上采用自增字段做主键。
解题思路:InnoDB 主键选择
考察点:InnoDB 索引数据结构,MySQL应用技能
分类:MySQL{校招,社招}
难度分级:P4,P5
6、数据库为什么要使用MVCC,使用MVCC有什么缺点?
Multi-Version Concurrency Control 多版本并发控制,因为锁机制是一种预防性的,读会阻塞写,写也会阻塞读,当锁定粒度较大,时间较长时并发性能就不会太好;而MVCC是一种后验性的,读不阻塞写,写也不阻塞读,等到提交的时候才检验是否有冲突,由于没有锁,所以读写不会相互阻塞,从而大大提升了并发性能。
缺点:通过MVCC机制,虽然让数据变得可重复读,但读到的数据可能是历史数据,是不及时的数据,不是数据库当前的数据!这在一些对于数据的时效特别敏感的业务中,就很可能出问题。
解题思路:MVCC
考察点:MySQL锁机制与MVCC
分类:MySQL、锁机制、MVCC{校招,社招}
难度分级:P5
7、如何分析慢查询,慢查询的分析步骤?
慢查询优化基本步骤
0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
3.order by limit 形式的sql语句让排序的表优先查
4.了解业务方使用场景
5.加索引时参照建索引的几大原则
6.观察结果,不符合预期继续从0分析
参考:http://tech.meituan.com/mysql-index.html
解题思路:
考察点:MySQL查询优化
分类:数据库,硬技能{校招,社招}
难度分级:P5
8、MySQL索引默认实现是用的什么数据结构,为什么采用这种?
B+树。
索引也是磁盘上的,磁盘的I/O存取的消耗是比内存高出几个数量级的,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数,所以要尽量降低树的高度。要降低树的高度,因此用多分支的树,并且要树的每层的节点尽量的多,B+树将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入,由于B+Tree内节点去掉了data域,因此可以拥有更大的出度,拥有更好的性能。
解题思路:MySQL索引数据结构
考察点:MySQL索引数据结构,B+树、B-树
分类:MySQL、数据结构{校招,社招}
难度分级:P4,P5
9、MySQL联合索引使用是有什么规则?如果对A,B,C做索引,那么SQL语句写成where C=X and B=X and A=X,是否还能用到该索引?如果SQL语句写成where A=X and B>X and C=X是否还能用到该索引?
联合索引有最左前缀匹配原则。
where C=X and B=X and A=X能用到该索引,因为=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,MySQL的查询优化器会优化成索引可以识别的形式。
where A=X and B>X and C=X可以用到该索引,但C是用不到索引的,因为MySQL会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。
解题思路:联合索引最左前缀匹配原则
考察点: 是否对联合索引的匹配原则,以及所以的数据结构有过了解
分类:MySQL索引基础知识{校招,社招}
难度分级:P4,P5
10、MySQL引擎MyISAM,InnoDB有什么区别,各有什么特点?
MySQL有多种存储引擎,MyISAM和InnoDB是其中常用的两种。这里介绍关于这两种引擎的一些基本概念(非深入介绍)。
MyISAM是MySQL的默认存储引擎,基于传统的ISAM类型,支持全文搜索,但不是事务安全的,而且不支持外键。每张MyISAM表存放在三个文件中:frm文件存放表格定义;数据文件是MYD (MYData);索引文件是MYI (MYIndex)。
InnoDB是事务型引擎,支持回滚、崩溃恢复能力、多版本并发控制、ACID事务,支持行级锁定(InnoDB表的行锁不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,如like操作时的SQL语句),以及提供与Oracle类型一致的不加锁读取方式。InnoDB存储它的表和索引在一个表空间中,表空间可以包含数个文件。
主要区别:
MyISAM是非事务安全型的,而InnoDB是事务安全型的。
MyISAM锁的粒度是表级,而InnoDB支持行级锁定。
MyISAM支持全文类型索引,而InnoDB不支持全文索引。
MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。
MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。
InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter table tablename type=innodb)。
应用场景:
MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。
InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。
解题思路:MyISAM和InnoDB引擎区别
考察点: MyISAM和InnoDB引擎的了解
分类:MySQL
难度分级:P5
11、从性能上考虑,MySQL InnoDB 表主键如何选择,为什么?
自增主键
InnoDB使用聚集索引,数据记录本身被存于主索引(一颗B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)。如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。这样就会形成一个紧凑的索引结构,近似顺序填满。由于每次插入时也不需要移动已有数据,因此效率很高,也不会增加很多开销在维护索引上。
如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置,此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。
因此,只要可以,请尽量在InnoDB上采用自增字段做主键。
解题思路:InnoDB主键选择
考察点:InnoDB索引数据结构,MySQL应用技能
分类:MySQL
12、MySQL InnoDB默认的事务隔离级别是什么?
Repeatable Read(可重复读)
解题思路:Repeatable Read(可重复读)
考察点:数据库事务了解
分类:MySQL、事务
难度分级:P4,P5
13、应用中怎么做到对数据库的读写分离,如何避免主从延迟造成的影响?
解题思路:
考察点:
分类:
难度分级:P5
14、如何处理组织机构树的联合查询问题?
树形结构来表征组织机构的关联关系,具体实现参考http://blog.csdn.net/monkey_d_meng/article/details/6647488 http://blog.csdn.net/biplusplus/article/details/7433625
解题思路:
考察点:
分类:难度分级:P5
15、MySQL Explain分析,给面试者一段具体的SQL Explain
参考答案:http://wiki.sankuai.com/x/-JMRBQ
解题思路:
考察点:
分类:
难度分级:P5
16、如何删除一个表的重复数据
参考答案:
CREATE TABLE `poi_menu_test` ( `id` bigint(30) NOT NULL AUTO_INCREMENT COMMENT 'ID', `poi_id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'id', `name` varchar(64) NOT NULL DEFAULT '' COMMENT '', PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17621402 DEFAULT CHARSET=utf8 COMMENT='poi';
delete pmt from poi_menu_test pmt inner join ( select id, poi_id, name from poi_menu_test group by poi_id, name having count(*) > 1 ) tmp on tmp.poi_id = pmt.poi_id and tmp.name = pmt.name and tmp.id <> pmt.id;
解题思路:
考察点:
分类:社招
难度分级:P5
17、数据库事物隔离级别,每种的特点;Spring事物传播与隔离级别的关系,如果配置的。
在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。数据库锁也是为了构建这些隔离级别存在的。
四种隔离级别:
未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别(不重复读)
可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
Spring的事务隔离级别:
ISOLATION_DEFAULT:使用数据库默认的隔离级别。
ISOLATION_READ_UNCOMMITTED:允许读取改变了的还未提交的数据,可能导致脏读、不可重复读和幻读。
ISOLATION_READ COMMITTED:允许并发事务提交之后读取,可以避免脏读,可能导致重复读和幻读。
ISOLATION_REPEATABLE_READ:对相同字段的多次读取结果一致,可导致幻读。
ISOLATION_SERIALIZABLE:完全服从ACID的原则,确保不发生脏读、不可重复读和幻读。可以根据自己的系统对数据的要求采取适应的隔离级别,因为隔离牵涉到锁定数据库中的记录,对数据正性要求越严格,并发的性能也越差。
Spring的事务传播行为:
Spring事务的传播行为说的是当一个方法调用另一个方法时,事务该如何操作。
PROPAGATION_MANDATORY:该方法必须运行在一个事务中。如果当前事务不存在则抛出异常。
PROPAGATION_NESTED:如果当前存在一个事务,则该方法运行在一个嵌套的事务中。被嵌套的事务可以从当前事务中单独的提交和回滚。如果当前不存在事务,则开始一个新的事务。各厂商对这种传播行为的支持参差不齐,使用时需注意。
PROPAGATION_NEVER:当前方法不应该运行在一个事务中。如果当前存在一个事务,则抛出异常。
PROPAGATION_NOT_SUPPORTED:当前方法不应该运行在一个事务中。如果一个事务正在运行,它将在该方法的运行期间挂起。
PROPAGATION_REQUIRED:该方法必须运行在一个事务中。如果一个事务正在运行,该方法将运行在这个事务中。否则,就开始一个新的事务。
PROPAGATION_REQUIRES_NEW:该方法必须运行在自己的事务中。它将启动一个新的事务。如果一个现有的事务正在运行,将在这个方法的运
PROPAGATION_REQUIRES_NEW:该方法必须运行在自己的事务中。它将启动一个新的事务。如果一个现有的事务正在运行,将在这个方法的运行期间挂起
PROPAGATION_SUPPORTS:当前方法不需要事务处理环境,但如果一个事务已经在运行的话,这个方法也可以在这个事务里运行。
配置方法:
<property name="transactionAttributes"> <props> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="delete*">PROPAGATION_REQUIRED</prop> <prop key="get*">PROPAGATION_REQUIRED, readOnly</prop> <prop key="find*">PROPAGATION_REQUIRED, ISOLATION_READ_UNCOMMITTED</prop> </props> </property> |
解题思路:
考察点:
分类:数据库,Spring
难度分级:P5
18、如何优化查询语句,explain每个字段的含义,如果优化OR语句,IN会用到索引吗?创建索引的顺序
explain每个字段含义:MySQL explain学习总结
or语句:where语句里如果带有or条件,是可以用到索引,前提是有针对or条件所有字段的单独索引
in会用到索引
优化or语句:or条件建立单独索引,用union替代
解题思路:
考察点:MySQL索引,优化
分类:MySQL
难度分级:P5
19、每个表的数据大小是否有限制,数据过大会对性能造成影响吗?常见的一些分库分表策略
参考答案:MySQL单表大小限制可以参考这篇文章:http://database.51cto.com/art/201011/234308.htm
这里引用一个问题为什么要分库分表呢?MySQL处理不了大的表吗?其实是可以处理的大表的。项目中单表物理上文件大小在80G多,单表记录数在5亿以上,而且这个表属于一个非常核用的表:朋友关系表。
但这种方式可以说不是一个最佳方式。因为面临文件系统如Ext3文件系统对大于大文件处理上也有许多问题,这个层面可以用xfs文件系统进行替换,但MySQL单表太大后有一个问题是不好解决:表结构调整相关的操作基本不在可能,所以大项在使用中都会面监着分库分表的应用。
从Innodb本身来讲数据文件的Btree上只有两个锁,叶子节点锁和子节点锁,可想而知,当发生页拆分或是添加新叶时都会造成表里不能写入数据。
关于分表分库策略:http://my.oschina.net/cmcm/blog/175104
解题思路:
考察点:MySQL
分类:MySQL
难度分级:P5 P6
20、如何实现主从分离,如何解决主从不一致带来的问题?
参考:数据库读写分离相关调研
解题思路:
考察点:MySQL
分类:MySQL
难度分级:P5 P6
21、数据库锁,如何防止覆盖更新,如何用数据库模拟队列?
参考答案:
覆盖更新可以用for update和where要更新的字段=更新之前的状态,判断影响记录数来实现。模拟队列需要考虑类型,优先级字段,同时用锁来避免重复同一条记录
解题思路:
考察点:MySQL,数据库
分类:数据库
难度分级:P5 P6
22、什么是乐观锁与悲观锁,如何实现乐观锁?
悲观锁:指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
乐观锁:乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
--补充,当主键采用数据库表生成时(GenerationType.TABLE),采用乐观锁好还是采用悲观锁好?这个要考虑决具体的策略?
一般来讲,会根据数据生长速度(考察点,需要考虑)来申请一定数量的主键ID,比如100个,这样可以最大限度的增加主键生成的效率,减少不必要的数据库交互。
这样即使在集群环境下,表访问的竞争压力并不大,因此采用悲观锁就可以;而且乐观锁并不一定能够防止数据的"脏写",会导致主键重复的情况发生。要说明的是,MVCC的实现没有固定的规范,每个数据库都会有不同的实现方式,这里讨论的是InnoDB的MVCC。
参考:Innodb中的事务隔离级别和锁的关系
解题思路:
考察点:悲观锁 乐观锁
分类:数据库
难度分级:P5
23、数据库连接池有哪些重要配置参数,分别起到什么作用?
标准答案:
C3P0 拥有比 DBCP 更丰富的配置属性,通过这些属性,可以对数据源进行各种有效的控制:
acquireIncrement:当连接池中的连接用完时,C3P0 一次性创建新连接的数目;
acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30;
acquireRetryDelay:两次连接中间隔时间,单位毫秒,默认为1000;
autoCommitOnClose:连接关闭时默认将所有未提交的操作回滚。默认为 false;
automaticTestTable:C3P0 将建一张名为 Test 的空表,并使用其自带的查询语句进行测试。如果定义了这个参数,那么属性 preferredTestQuery 将被忽略。不能在这张 Test 表上进行任何操作,它将中为 C3P0 测试所用,默认为 null;
breakAfterAcquireFailure:获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用 getConnection() 的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为 false;
checkoutTimeout:当连接池用完时客户端调用 getConnection() 后等待获取新连接的时间,超时后将抛出 SQLException,如设为 0 则无限期等待。单位毫秒,默认为 0;
connectionTesterClassName:通过实现 ConnectionTester 或 QueryConnectionTester 的类来测试连接,类名需设置为全限定名。默认为com.mchange.v2.C3P0.impl.DefaultConnectionTester ;
idleConnectionTestPeriod:隔多少秒检查所有连接池中的空闲连接,默认为 0 表示不检查;
initialPoolSize:初始化时创建的连接数,应在 minPoolSize 与 maxPoolSize 之间取值。默认为 3;
maxIdleTime:最大空闲时间,超过空闲时间的连接将被丢弃。为 0 或负数则永不丢弃。默认为 0;
var flag=false;
function checkForm(){
if (flag==true){
return false;
}
flag=true;
document.form1.submit();
}
maxIdleTime:最大空闲时间,超过空闲时间的连接将被丢弃。为 0 或负数则永不丢弃。默认为 0;
maxPoolSize:连接池中保留的最大连接数。默认为 15;
maxStatements:JDBC的标准参数,用以控制数据源内加载的 PreparedStatement 数量。但由于预缓存的 Statement 属于单个 Connection 而不是整个连接池。所以设置这个参数需要考虑到多方面的因素,如果 maxStatements 与 maxStatementsPerConnection 均为 0 ,则缓存被关闭。默认为 0;
maxStatementsPerConnection:连接池内单个连接所拥有的最大缓存 Statement 数。默认为 0;
numHelperThreads:C3P0 是异步操作的,缓慢的 JDBC 操作通过帮助进程完成。扩展这些操作可以有效的提升性能,通过多线程实现多个操作同时被执行。默认为 3;
preferredTestQuery:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个参数能显著提高测试速度。测试的表必须在初始数据源的时候就存在。默认为 null;
propertyCycle:用户修改系统配置参数执行前最多等待的秒数。默认为 300;
testConnectionOnCheckout:因性能消耗大请只在需要的时候使用它。如果设为 true 那么在每个 connection 提交的时候都将校验其有效性。建议使用 idleConnectionTestPeriod 或 automaticTestTable等方法来提升连接测试的性能。默认为 false;
estConnectionOnCheckin:如果设为 true 那么在取得连接的同时将校验连接的有效性。默认为 false。
解题思路:同答案
考察点:数据库连接池
分类:Spring,MySQL{社招}
难度分级:P5,P6
二、Java相关
2.1、Java基础知识
1、equals方法有什么特性?
1、为什么重载了equals方法之后需要去重载hashCode方法?
A:Hash的时候需要保证equals的对象映射到同一个位置
2、重载和覆盖的区别
3、static关键字的用法
静态方法和静态代码块
static和非static的区别,一般什么情况下会使用,怎么去用;
什么时候执行;
加分 —— 实例变量,静态变量,构造函数、父类相对变量的执行过程。
4、final finalize finally 关键字都是干什么的?
Java中的final类:String、Integer、Math、UUID
5、String StringBuffer StringBuilder 区别
StringBuffer和StringBuilder是线程安全问题,String和StringBuilder是效率以及内存分配问题。
加分1:String和StringBuilder在编译优化之后结果基本一致;
加分2:String在循环中操作,回导致声明很多的StringBuilder,因此禁止这种操作;
6、HashMap的实现原理
基本:底层数据结构,冲突解决(Hash值映射到同一个位置——List,再次Hash)
加分:链表解决冲突的情况下如何优化速度?List长度超过一个值,转化为树形数据结构,如JDK8中,链表长度超过8,转换为红黑树。
加分:ConcurrentHashMap的实现——Segment。
加分:ConcurrentModificationException异常发生的原因
7、wait和sleep的区别
前者要求必须先获得锁才能调用,后者不需要。前者调用后会释放锁(进入等待池),后者不存在释放问题。
8、synchronized关键字修饰静态方法和实例方法的区别
加锁对象不一样。
加分:和volatile关键字的区别?volatile只管可见性(对于基本类型变量的赋值可以保证线程安全),synchronized同时保证互斥性
9、死锁的四个条件
1)互斥条件:一个资源每次只能被一个进程使用
2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺
4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
10、notify和notifyAll的区别
除唤醒线程外,前者调用后其余线程还在等待池,后者其余线程进入锁池(可以主动竞争锁)
11、Java 泛型的实现原理是什么?
基本:概念用法,T ? extends
加分:泛型擦除,如下:编译报错还是运行报错
public void test(List<Integer> li){ }
public void test(List<String> ls){ }
12、浅克隆和深克隆?深克隆的方法?
13、Integer缓存?Integer比较大小注意问题。==和equals的区别考察
Integer是有缓冲池的,java.lang.Integer.valueOf(int)方法默认情况下如果参数在-128到127之间,则返回缓存中的对象,否则返回new Integer(int)。Java使用该机制是为了达到最小化数据输入和输出的目的,这是一种优化措施,提高效率
其他的包装器:
Boolean:(全部缓存)
Byte:(全部缓存)
Character ( <=127 缓存)
Short (-128~127 缓存)
Long (-128~127 缓存)
Float (没有缓存)
Double (没有缓存)
可以设置系统属性 java.lang.Integer.IntegerCache.high 修改缓冲区上限,默认为127。参数内容应为大于127的十进制数形式的字符串,否则将被忽略。取值范围为127-Long.MAX_VALUE,但是用时将强转为int。当系统中大量使用Integer时,增大缓存上限可以节省小量内存。
区别“==”和equals():“==”是比较两个对象是不是引用自同一个对象。“equals()”是比较两个对象的内容。
14、装箱和解箱机制?
http://developer.51cto.com/art/201203/325314.htm
15、Java的类加载器体系结构和双亲委托机制
http://blog.csdn.net/lovingprince/article/details/4317069
加分:jetty or tomcat的类加载机制
16、简述一下Java的异常体系,什么是受检异常?什么是运行时异常?封装一个API的时候什么情况下抛出异常?
17、String常量池的考核
String s1 = new String("hello");
String s2 = "hello";
区别http://blog.sina.com.cn/s/blog_5203f6ce0100tiux.html
18、Hash冲突,有哪几种常见的解决方法,Java中HashMap用的是哪一种?
1)开放定址法
2)拉链法;http://www.cnblogs.com/jillzhang/archive/2006/11/03/548671.html,Java中HashMap使用的是拉链法。
解题思路:基础知识
考察点:Hash冲突,HashMap的底层数据结构实现
分类:数据结构,Java集合框架
难度分级:P4
19、HashMap,已知整个生命周期内不会放入超过100个元素,那么占用内存大小最优且设置完初始值后无需自动扩容,该初始值应该设置为多少?
如果默认使用HashMap内置的负载因子loadFactor为0.75。鉴于HashMap初始化设置大小为2的n次方,则100/0.75=133,大于133的最小2的n次方为256个。
解题思路:基础知识
考察点:考察HashMap的负载因子以及构造函数的理解
分类:Java集合框架{校招,社招}
难度分级:P4
20、HashMap在高并发情况下不当使用,可能会导致什么样极端情况,为什么?
在并发的多线程使用场景中,在resize扩容的时候,使得HashMap形成环链,造成死循环,CPU飙升至100%。可以举例子说明。
参考链接:http://wiki.sankuai.com/x/oUl9BQ
考察点:HashMap的并发性
分类:{校招,社招}
21、HashMap/ConcurrentHashMap的数据结构实现,扩容机制,HashMap hash的冲突处理。
答案参考前面。补充:扩容机制考察扩容时机,扩容容量变化机制,扩容具体实现步骤-源码resize()函数,扩容时还要rehash()。
加分:ConcurrentHashMap不允许存储null的key和value,无法区分时并发时删除掉key还是本来就是null值。
解题思路:
考察点:HashMap/ConcurrentHashMap的底层实现
分类:{校招,社招}
难度分级:P4,P5
22、LinkedHashMap数据结构实现
参考答案:
首先搞清楚HashMap得实现;然后重点考察和HashMap的区别。LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。最好画个图解释下。Entry对象在HashMap的时候包含key、value、hash值,以及一个next;而在LinkedHashMap中新增了before和after。
解题思路:基础知识
考察点:HashMap和LinkedHashMap的数据结构的区别
分类:{校招,社招}
难度分级:P4,P5
23、ArrayList和LinkedList的区别,数据结构实现,扩容机制
(1) ArrayList是实现了基于动态数组的数据结构,LinkedList基于双向循环链表的数据结构。
(2) 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
(3) 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
(4) 查找操作indexOf,lastIndexOf,contains等,两者差不多。
(5) 随机查找指定节点的操作get,ArrayList速度要快于LinkedList。当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList。
扩容:针对ArrayList,在新增的时候,容量不够就需要扩容,2倍。
解题思路:基础知识
考察点:ArrayList和LinkedList的区别,ArrayList的扩容
分类:{校招,社招}
难度分级:P4
24、哈希函数的构造方法常见的有哪几种?
(1)直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b为散列函数。若其中H(key)中已经有值了,就往下一个找,直到H(key)中没有值了,就放进去。
(2)数字分析法:就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址。
(3)平方取中法:取关键字平方后的中间几位作为散列地址。
(4)折叠法:将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(去除进位)作为散列地址。
(5)随机数法:选择一随机函数,取关键字的随机值作为散列地址,通常用于关键字长度不同的场合。
(6)除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p,p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。
解题思路:基础知识
考察点:哈希函数的构造方法
分类:数据结构
难度分级:P4
25、HashMap的实现原理(内部数据结构,null key,rehash);为什么说hashMap不是线程安全的。
(1)内部数据结构实现:数组+链表(最好画个图能解释清楚)
(2)key值不能重复,只能put一个key为null的键值对。可以更深层次考察对put(null, value)以及get(null)的理解。用于存放默认值?
(3)HashMap在put时,经过了两次hash,一个是JDK自带的对对象key的hash,然后再对结果使用HashMap内部函数hash(int h);hash(int h)方法根据key的hashCode重新计算一次散列。可以更深入考察对hash(int h)以及indexFor(int h, int length)两个函数的理解。
(4)在put时如果空间不够就需要扩容resize(),考察扩容过程--重新计算复制。
(5)在并发的多线程使用场景中,使用HashMap形成环链,造成死循环,CPU飙升至100%。例子见链接。http://wiki.sankuai.com/pages/viewpage.action?pageId=89609604
解题思路:基础知识
考察点:考察HashMap的底层实现远离
分类:Java集合框架{校招,社招}
难度分级:P4
26、List有哪些实现,实现原理,如何选择实现;是否比较过性能差异?contains方法是怎么比较对象的?
参考答案:
(1)List的直接实现是两个抽象类,AbstractList和AbstractSequentialList。其中,AbstractList为随即访问(如数组)实现方案提供尽可能的封装,AbstractSequentialList为连续访问(如链表)实现方案提供了尽可能的封装。
ArrayList,直接父类是AbstractList,数据结构是大小可变的数组,它不是同步的。
LinkedList,直接父类是AbstractSquentialList,数据结构是双向链表,它不是同步的,它同时实现了Deque(双向队列)和Queue(队列)接口。同时它还提供了push和pop这两个堆栈操作的接口。
Vector,直接父类是AbstractList,特性和ArrayList一样,只是它是线程同步的。
Stack,直接父类是Vector,实现堆栈这种数据结构。
(2)通过对象的equals方法。
解题思路:基础知识
考察点:考察对List的理解和运用以及equals的理解
分类:Java集合框架{校招,社招}
难度分级:P4
27、如何拷贝数组,怎样效率最高?为什么?
(1)使用循环结构这种方法最灵活。唯一不足的地方可能就是代码较多
(2)使用Object类的clone()方法,这种方法最简单,得到原数组的一个副本。灵活形也最差。效率最差,尤其是在数组元素很大或者复制对象数组时。
(3)使用Systems的arraycopy这种方法被告之速度最快,并且灵活性也较好,可以指定原数组名称、以及元素的开始位置、复制的元素的个数,目标数组名称、目标数组的位置。
浅拷贝和深拷贝得理解:定义一个数组int[] a={3,1,4,2,5}; int[] b=a; 数组b只是对数组a的又一个引用,即浅拷贝。如果改变数组b中元素的值,其实是改变了数组a的元素的值,要实现深度复制,可以用clone或者System.arrayCopy clone和System.arrayCopy都是对一维数组的深度复制;因为Java中没有二维数组的概念,只有数组的数组。所以二维数组a中存储的实际上是两个一维数组的引用。当调用clone函数时,是对这两个引用进行了复制。
解题思路:基础知识
考察点:数组拷贝,浅拷贝和深拷贝的区别
分类:{校招,社招}
难度分级:P4,P5
2.2、JVM
1、JVM堆内存区,有两个S区有什么作用?
S区即 Survivor 区,位于年轻代。年轻代分三个区。一个 Eden 区,两个 Survivor 区。大部分对象在 Eden 区中生成。当 Eden 区满时,还存活的对象将被复制到 Survivor 区(两个中的一个),当这个 Survivor 区满时,此区的存活对象将被复制到另外一个 Survivor 区,当这个 Survivor 区也满了的时候,从第一个Survivor 区复制过来的并且此时还存活的对象,将被复制年老区(Tenured)。需要注意,Survivor 的两个区是对称的,没有先后关系,所以同一个区中可能同时存在从 Eden 复制过来对象,和从前一个 Survivor 复制过来的对象,而复制到年老区的只有从第一个 Survivor 区过来的对象。而且,Survivor 区总有一个是空的。
解题思路:基础知识
考察点:JVM 内存结构
分类:JVM GC {校招,社招}
难度分级:P4
2、有遇到过OOM么,什么情况,怎么发现的,怎么查原因的?
参考答案:
http://outofmemory.cn/c/java-outOfMemoryError(OOM分类) http://www.51testing.com/html/92/77492-203728.html(jstat使用) http://my.oschina.net/shaorongjie/blog/161385(MAT使用)
解题思路:基础知识
考察点:jstat mat工具 jvm
分类:JVM GC {校招,社招}
难度分级:P4,P5
3、内存分配策略
1)对象优先在Eden分配
2)大对象直接进入老年代
3)长期存活的对象将进入老年代
4)动态对象年龄判定
参考:http://www.cnblogs.com/liangzh/archive/2012/07/03/2575252.html
解题思路:基础知识
考察点:JVM
分类:JVM GC {校招,社招}
难度分级:P4,P5
4、线程内存和主内存是如何交互的?
假设某条线程执行一个synchronized代码段,其间对某变量进行操作,JVM会依次执行如下动作:
(1)获取同步对象monitor (lock)
(2)从主存复制变量到当前工作内存 (read and load)
(3)执行代码,改变共享变量值 (use and assign)
(4)用工作内存数据刷新主存相关内容 (store and write) (5) 释放同步对象锁 (unlock)
解题思路:基础知识
考察点: JVM
分类:JVM GC {校招,社招}
难度分级:P4,P5
5、线上环境JVM参数Xms Xmx是如何设置的,如果大小是一致,为什么这样设置?
Xmx设置 JVM 的最大可用内存,Xms 设置 JVM 实际使用内存,一般 Xmx和 Xms 相同,这是因为当 Xmx 内存空间不够用时,将进行扩容导致Full GC。将 Xmx 和 Xms 设置成相同的值,避免因 Xms 偏小导致频繁重新分配内存,影响应用使用。
解题思路:基础知识
考察点:JVM
分类:JVM GC {校招,社招}
难度分级:P4,P5
6、如何定位一个CPU Load过高的Java线程?
(1)jps -v列出所有的Java进程,top找出cpu占用过高的对应的Java 进程pid
(2)使用top -H -p PID 命令查看对应进程里的哪个线程占用CPU过高,取该线程pid
(3)将线程的 pid 转成16进制
(4)jstack [进程pid] | grep -A 100 [线程pid的16进制] dump出jvm该线程的后100行,或者整个输出到文件 jstack -l pid > xxxfile
参考文献:Crm线上机器发布时load过高案例分析阶段总结以及监控工具介绍
解题思路:基础知识
考察点: JVM
分类:JVM GC {社招}
难度分级:P5
7、JmapDump的文件大小和MAT分析工具里显示的大小不一致一般是什么原因导致的?
JmapDump的文件一般比MAT工具大。创建索引期间,MAT会删除部分垃圾收集算法遗留下的不可达的object,因为回收这些较小的object代价较大,一般这些object占比不超过4%。另外不能正确的写JmapDump的文件。尤其在较老的JVM(1.4,1.5)并且使用jmap命令获取 JmapDump 的文件的时候。
解题思路:基础知识
考察点:JVM
分类:JVM GC {社招}
难度分级:P5
8、如何在Jmap未响应的情况下Dump出内存?
加-F参数,强制执行
解题思路:基础知识
考察点: JVM
分类:JVM GC {社招}
难度分级:P5
9、说出5个JVM的参数以及含义,怎么样配置这样参数?
JVM参数设置
解题思路:基础知识
考察点:JVM
分类:JVM GC {校招,社招}
难度分级:P4,P5
10、JVM的一些健康指标和经验值,如何配置最优?
开发性试题,偏社招,考察面试者对系统的掌控力,一般都会从垃圾回收的角度来解释,比如用jstat或者gc日志来看ygc的单次时间和频繁程度,full gc的单次时间和频繁程度;ygc的经验时间100ms以下,3秒一次;full gc 1秒以下,1小时一次,每次回收的比率70%等等,也会用jstack和jmap看系统是否有太多的线程和不必要的内存等等。
关于如何才能让配置最优,有一些理论支撑,比如高吞吐和延迟低的垃圾收集器选择,比如高并发对象存活时间不长,可以适当加大young区;但是有经验的面试者会调整一些参数测试来印证自己的想法。
解题思路:基础知识
考察点:JVM
分类:JVM GC {校招,社招}
难度分级:P4,P5
11、对象存活算法,常见的有哪几种,Java采用的是哪种?
(1)引用计数算法
原理:给对象添加一个引用计数器,每当有一个地方引用它时,计数器加1;引用失效时,计数器减1;计数器为0说明可被回收。
缺点:很难解决对象相互循环引用的问题。
(2)可达性分析算法
原理:通过一些列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
在Java语言中,可作为GC Roots的对象包括下面几种:虚拟机栈(栈帧中的本地变量表)中引用的对象、方法区类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象。
参考文献:http://blog.csdn.net/chaofanwei/article/details/19541421
解题思路:基础知识
考察点:JVM GC
分类:JVM GC {校招,社招}
难度分级:P4,P5
12、JVM新生代ParNew用的是什么垃圾收集算法?
参考答案:复制
解题思路:基础知识
考察点:JVM GC
分类:JVM GC {校招,社招}
难度分级:P4,P5
13、JVM垃圾收集复制算法中,Eden : Form : To,为什么是8 : 1 : 1,而不是1 : 1 : 1?
一般情况下,新生代中的对象大多生命周期很短,也就是说当进行垃圾收集时,大部分对象都是垃圾,只有一小部分对象会存活下来,所以只要保留一小部分内存保存存活下来的对象就行了。
在新生代中一般将内存划分为三个部分:一个较大的Eden空间和两个较小的Survior空间(一样大小),每次使用Eden和一个Survior的内存,进行垃圾收集时将Eden和使用的Survior中的存活的对象复制到另一个Survior空间中,然后清除这两个空间的内存,下次使用Eden和另一个Survior,HotSpot中默认将这三个空间的比例划分为8 : 1 : 1,这样被浪费掉的空间就只有Eden区内存的1/10。
参考文献:http://www.cnblogs.com/angeldevil/p/3803969.html
解题思路:基础知识
考察点:JVM GC
分类:JVM GC {校招,社招}
难度分级:P4,P5
14、JVM老年代CMS用的是什么垃圾收集算法?
CMS以获取最短回收停顿时间为目标的收集器,使用“标记-清除”算法,分为以下6个步骤
1.STW initial mark:第一次暂停,初始化标记,从root标记old space存活对象(the set of objects reachable from roots (application code))
2.Concurrent marking:运行时标记,从上一步得到的集合出发,遍历old space,标记存活对象(all live objects that are transitively reachable from previous set)
3.Concurrent precleaning:并发的标记前一阶段被修改的对象(card table)
4.STW remark:第二次暂停,检查,标记,检查脏页的对象,标记前一阶段被修改的对象(revisiting any objects that were modified during the concurrent marking phase)
5.Concurrent sweeping:运行过程中清理,扫描old space,释放不可到达对象占用的空间
6.Concurrent reset:此次CMS结束后,重设CMS状态等待下次CMS的触发
或者4个大步骤:1、初始标记(initial mark) 2、并发标记(concurrent mark) 3、remark 4、并发清除(concurrent sweep)
解题思路:基础知识
考察点:JVM GC
分类:JVM GC {校招,社招}
难度分级:P4,P5
15、young gc和full gc触发条件
young gc:eden空间不足
full gc:显示调用System.gc()、旧生代空间不足、Permanent Generation空间满、CMS GC时出现promotion failed和concurrent mode failure、RMI等的定时触发、YGC时的悲观策略、dump live的内存信息时
参考文献:http://blog.csdn.net/vernonzheng/article/details/8460128 http://blog.sina.com.cn/s/blog_7581a4c301019hsc.html
解题思路:基础知识
考察点:JVM GC
分类:JVM GC {校招,社招}
难度分级:P4,P5
16、垃圾回收器种类,垃圾回收扫描算法(root扫描),回收算法(复制,标记清除,标记整理)
垃圾回收器种类:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS(Concurrent Mark Sweep)、G1(Garbage First)
垃圾回收扫描算法:
垃圾检测通过建立一个根对象的集合(局部变量、栈桢中的操作数,在本地方法中引用的对象,常量池等)并检查从这些根对象开始的可触及性来实现。根对象总是可访问的,如果存在根对象到一个对象的引用路径,那么称这个对象是可触及的或活动对象,否则是不可触及的,不可触及的对象就是垃圾对象。
回收算法:
标记-清除
分为标记和清除两个阶段,在标记阶段,垃圾收集器跟踪从根对象的引用,在追踪的过程中对遇到的对象打一个标记,最终未被标记的对象就是垃圾对象,在清除阶段,回收垃圾对象占用的内存。可以在对象本身添加跟踪标记,也可以用一个独立的位图来设置标记。标记清除法是基础的收集算法,其他算法大多时针对这个算法缺点的改进。存在效率和碎片问题。复制算法
将内存划分为大小相等的两个区域,每次只使用其中的一个区域,当这个区域的内存用完了,就将可触及的对象直接复制到新的区域并连续存放以消除内存碎片,当可触及对象复制完后,清除旧内存区域,修改引用的值。这种算法明显缺点是浪费内存,故实际使用中常将新生代划分成8:1:1三个区。
标记整理
标记整理算法中标记的过程同标记清理一样,但整理部分不是直接清除掉垃圾对象,而是将活动对象统一移动一内存的一端,然后清除边界外的内存区域,这样就避免了内存碎片。也不会浪费内存,不需要其他内存进行担保。
分代收集
大多数程序中创建的大部分对象生命周期都很短,而且会有一小部分生命周期长的对象,为了克服复制收集器中每次垃圾收集都要拷贝所有的活动对象的缺点,将内存划分为不同的区域,更多地收集短生命周期所在的内存区域,当对象经历一定次数的垃圾收集存活时,提升它的存在的区域。一般是划分为新生代和老年代。新生代又划分为Eden区,From Survior区和To Survior区。
自适应收集器监听堆中的情形,并且对应地调用合适的垃圾收集技术。
参考文献:http://www.cnblogs.com/angeldevil/p/3803969.html
解题思路:基础知识
考察点:JVM GC
分类:JVM GC {校招,社招}
难度分级:P4,P5
17、CMS回收器的标记过程,内存碎片问题
CMS回收器的标记过程:
1.STW initial mark:第一次暂停,初始化标记,从root标记old space存活对象(the set of objects reachable from roots (application code))
2.Concurrent marking:运行时标记,从上一步得到的集合出发,遍历old space,标记存活对象(all live objects that are transitively reachable from previous set)
3.Concurrent precleaning:并发的标记前一阶段被修改的对象(card table)
4.STW remark:第二次暂停,检查,标记,检查脏页的对象,标记前一阶段被修改的对象(revisiting any objects that were modified during the concurrent marking phase)
内存碎片问题:
CMS基于“标记-清除”算法,进行垃圾回收后会存在内存碎片,当申请大的连续内存时可能内存不足,此时需要进行一次Full GC,可以通过参数指定进行Full GC后或进行多少次Full GC后进行一次内存压缩来整理内存碎片。
解题思路:基础知识
考察点:JVM GC
分类:JVM GC {校招,社招}
难度分级:P4,P5
18、GC Log分析
参考 JVM gc介绍和日志说明
19、画一下JVM内存结构图,各块分别有什么的作用?
程序计数器:是一块很小的内存区域,主要作用是记录当前线程所执行的字节码的行号。
虚拟机栈:是线程私有的,存放当前线程中局部基本类型的变量、部分的返回结果以及Stack Frame,非基本类型的对象的引用。Sun JDK的实现中JVM栈的空间是在物理内存上分配的,而不是从堆上分配。
堆:存储对象实例和数组区域
方法区域:是全局共享的,存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息。
本地方法栈:支持native方法的执行,存储每个native方法调用的状态。
解题思路:基础知识
考察点:JVM
分类:JVM {校招,社招}
难度分级:P4,P5
20、JVM垃圾收集在ParNew+CMS条件下,哪些情况下会让JVM认为产生了一次FULL GC?
JVM认为在老年代或者永久区发生的gc行为就是Full GC,在ParNew+CMS条件下,发生Full GC的原因通常为:
(1) 永久区达到一定比例。
(2) 老年代达到一定比例。
(3) 悲观策略。
(4) System.gc(),jmap -dump:live,jmap -histo:live 等主动触发的。
解题思路:
考察点:GC收集器 Full GC
分类:JVM GC {校招,社招}
难度分级:P4,P5
21、如何查看StringPool大小?
通过Java -XX:+PrintStringTableStatistics命令查看,Number of buckets显示的就是StringPool的默认大小,在jdk7 u40版本以前,它的默认大小是1009,之后便调整为60013。
解题思路:JVM基础知识
考察点:StringPool
分类:JVM GC {校招,社招}
难度分级:P4,P5
22、JmapDump的文件中是否包括StringPool?
StringPool在jdk6中是在永久区,dump heap时,无法输出。在jdk7中,stringpool移到heap中,可以输出。
解题思路:JVM基础知识
考察点:StringPool
分类:JVM GC {校招,社招}
难度分级:P4,P5
2.3、多线程
1、Java线程有哪些状态?并画出状态转换图
-
- NEW:初始状态,线程被构建,但是还没有调用start方法。
- RUNABLE:运行状态,此状态分为就绪和运行两种状态,统称为“运行中”。
- BLOCKED:阻塞状态,标识线程阻塞于锁。
- WAITING:等待状态,标识线程进入等待状态。
- TIME_WAITING:超时等待,该状态不同于WAITING,它是可以在指定的事件自行返回的。
- TERMINATED:终止状态,表示当前线程已经执行完毕。
2、举例Java线程安全的实现方法,原理、区别?
互斥同步:synchronized关键字,Lock ReentrantLock,volatile
非阻塞同步:原子变量 AtomicLong 原理:sun.misc.Unsafe类,CAS指令
解题思路:基础知识
考察点:Java 线程
分类:Java 线程 {校招,社招}
难度分级:P4,P5
3、多次调用线程的start方法会怎么样?
线程的start()只能被调用一次,否则会报java.lang.IllegalThreadStateException
Thread类的start()方法
/** * Causes this thread to begin execution; the Java Virtual Machine * calls the run() method of this thread. * * The result is that two threads are running concurrently: the * current thread (which returns from the call to the * start() method) and the other thread (which executes its * run() method). * * It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. * * @exception IllegalThreadStateException if the thread was already started. * @see #run() * @see #stop() */ public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } |
解题思路:基础知识
考察点:Java 线程
分类:Java 线程 {校招,社招}
难度分级:P4,P5
4、线程sleep()和yield()的区别是什么?
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;
yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;
yield()只能使同优先级的线程有执行的机会。
解题思路:基础知识
考察点:Java 线程
分类:Java 线程 {校招,社招}
难度分级:P4,P5
5、线程同步问题
(1)在需要同步的方法的方法签名中加入synchronized关键字。
(2)使用synchronized块对需要进行同步的代码段进行同步。
(3)使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象。
(4)锁的粒度:类级别和对象级别
(5)读写锁,CountDownLatch等的使用
(6)Lock 与 synchronized 的区别
http://houlinyan.iteye.com/blog/1112535
解题思路:基础知识
考察点:Java 线程
分类:Java 线程 {校招,社招}
难度分级:P4,P5
6、wait()、notify()和notifyAll(),wait()和sleep()的区别
wait()和notify()的区别:http://blog.csdn.net/oracle_microsoft/article/details/6863662
/** * notify() Wakes up a single thread that is waiting on this object's * monitor. If any threads are waiting on this object, one of them * is chosen to be awakened. The choice is arbitrary and occurs at * the discretion of the implementation. A thread waits on an object's * monitor by calling one of the {@code wait} methods. * * The awakened thread will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened thread will * compete in the usual manner with any other threads that might be * actively competing to synchronize on this object; for example, the * awakened thread enjoys no reliable privilege or disadvantage in being * the next thread to lock this object. * * This method should only be called by a thread that is the owner * of this object's monitor. A thread becomes the owner of the * object's monitor in one of three ways: * * By executing a synchronized instance method of that object. * By executing the body of a {@code synchronized} statement * that synchronizes on the object. * For objects of type {@code Class,} by executing a * synchronized static method of that class. * * Only one thread at a time can own an object's monitor. * * @throws IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notifyAll() * @see java.lang.Object#wait() */ public final native void notify(); /** * notifyAll() Wakes up all threads that are waiting on this object's monitor. A * thread waits on an object's monitor by calling one of the * {@code wait} methods. * * The awakened threads will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened threads * will compete in the usual manner with any other threads that might * be actively competing to synchronize on this object; for example, * the awakened threads enjoy no reliable privilege or disadvantage in * being the next thread to lock this object. * * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @throws IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notify() * @see java.lang.Object#wait() */ public final native void notifyAll(); /** * Causes the current thread to wait until either another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object, or a * specified amount of time has elapsed. * * The current thread must own this object's monitor. * * This method causes the current thread (call it T) to * place itself in the wait set for this object and then to relinquish * any and all synchronization claims on this object. Thread T * becomes disabled for thread scheduling purposes and lies dormant * until one of four things happens: * * Some other thread invokes the {@code notify} method for this * object and thread T happens to be arbitrarily chosen as * the thread to be awakened. * Some other thread invokes the {@code notifyAll} method for this * object. * Some other thread {@linkplain Thread#interrupt() interrupts} * thread T. * The specified amount of real time has elapsed, more or less. If * {@code timeout} is zero, however, then real time is not taken into * consideration and the thread simply waits until notified. * * The thread T is then removed from the wait set for this * object and re-enabled for thread scheduling. It then competes in the * usual manner with other threads for the right to synchronize on the * object; once it has gained control of the object, all its * synchronization claims on the object are restored to the status quo * ante - that is, to the situation as of the time that the {@code wait} * method was invoked. Thread T then returns from the * invocation of the {@code wait} method. Thus, on return from the * {@code wait} method, the synchronization state of the object and of * thread {@code T} is exactly as it was when the {@code wait} method * was invoked. * * A thread can also wake up without being notified, interrupted, or * timing out, a so-called spurious wakeup. While this will rarely * occur in practice, applications must guard against it by testing for * the condition that should have caused the thread to be awakened, and * continuing to wait if the condition is not satisfied. In other words, * waits should always occur in loops, like this one: * * synchronized (obj) { * while (condition does not hold) * obj.wait(timeout); * ... // Perform action appropriate to condition * } * * (For more information on this topic, see Section 3.2.3 in Doug Lea's * "Concurrent Programming in Java (Second Edition)" (Addison-Wesley, * 2000), or Item 50 in Joshua Bloch's "Effective Java Programming * Language Guide" (Addison-Wesley, 2001). * * If the current thread is {@linkplain java.lang.Thread#interrupt() * interrupted} by any thread before or while it is waiting, then an * {@code InterruptedException} is thrown. This exception is not * thrown until the lock status of this object has been restored as * described above. * * Note that the {@code wait} method, as it places the current thread * into the wait set for this object, unlocks only this object; any * other objects on which the current thread may be synchronized remain * locked while the thread waits. * * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @param timeout the maximum time to wait in milliseconds. * @throws IllegalArgumentException if the value of timeout is * negative. * @throws IllegalMonitorStateException if the current thread is not * the owner of the object's monitor. * @throws InterruptedException if any thread interrupted the * current thread before or while the current thread * was waiting for a notification. The <i>interrupted * status</i> of the current thread is cleared when * this exception is thrown. * @see java.lang.Object#notify() * @see java.lang.Object#notifyAll() */ public final native void wait(long timeout) throws InterruptedException; /** * Causes the current thread to wait until another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object, or * some other thread interrupts the current thread, or a certain * amount of real time has elapsed. * * This method is similar to the {@code wait} method of one * argument, but it allows finer control over the amount of time to * wait for a notification before giving up. The amount of real time, * measured in nanoseconds, is given by: * 1000000*timeout+nanos * * In all other respects, this method does the same thing as the * method {@link #wait(long)} of one argument. In particular, * {@code wait(0, 0)} means the same thing as {@code wait(0)}. * * The current thread must own this object's monitor. The thread * releases ownership of this monitor and waits until either of the * following two conditions has occurred: * * Another thread notifies threads waiting on this object's monitor * to wake up either through a call to the {@code notify} method * or the {@code notifyAll} method. * The timeout period, specified by {@code timeout} * milliseconds plus {@code nanos} nanoseconds arguments, has * elapsed. * * The thread then waits until it can re-obtain ownership of the * monitor and resumes execution. * * As in the one argument version, interrupts and spurious wakeups are * possible, and this method should always be used in a loop: * * synchronized (obj) { * while (condition does not hold) * obj.wait(timeout, nanos); * ... // Perform action appropriate to condition * } * * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @param timeout the maximum time to wait in milliseconds. * @param nanos additional time, in nanoseconds range * 0-999999. * @throws IllegalArgumentException if the value of timeout is * negative or the value of nanos is * not in the range 0-999999. * @throws IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @throws InterruptedException if any thread interrupted the * current thread before or while the current thread * was waiting for a notification. The interrupted * status of the current thread is cleared when * this exception is thrown. */ public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && timeout == 0)) { timeout++; } wait(timeout); } /** * Causes the current thread to wait until another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object. * In other words, this method behaves exactly as if it simply * performs the call {@code wait(0)}. * * The current thread must own this object's monitor. The thread * releases ownership of this monitor and waits until another thread * notifies threads waiting on this object's monitor to wake up * either through a call to the {@code notify} method or the * {@code notifyAll} method. The thread then waits until it can * re-obtain ownership of the monitor and resumes execution. * * As in the one argument version, interrupts and spurious wakeups are * possible, and this method should always be used in a loop: * * synchronized (obj) { * while (condition does not hold) * obj.wait(); * ... // Perform action appropriate to condition * } * * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @throws IllegalMonitorStateException if the current thread is not * the owner of the object's monitor. * @throws InterruptedException if any thread interrupted the * current thread before or while the current thread * was waiting for a notification. The <i>interrupted * status</i> of the current thread is cleared when * this exception is thrown. * @see java.lang.Object#notify() * @see java.lang.Object#notifyAll() */ public final void wait() throws InterruptedException { wait(0); } |
wait()和sleep()的区别:
(1)sleep是Thread类的方法,是线程用来控制自身流程的,比如有一个要报时的线程,每一秒中打印出一个时间,那么就需要在print方法前面加上一个sleep让自己每隔一秒执行一次。就像个闹钟一样。
wait是Object类的方法,用来线程间的通信,这个方法会使当前拥有该对象锁的进程等待知道其他线程调用notify方法时再醒来,不过也可以给他指定一个时间,自动醒来。这个方法主要是用走不同线程之间的调度的。
(2)关于锁的释放,调用sleep方法不会释放锁(感觉是sleep方法本来就是和锁没有关系的,因为sleep是一个线程用于管理自己的方法,不涉及线程通信)
(3)使用区域:由于wait函数的特殊意义,所以应该放在同步语句块中的,这样才有意义。
注意:两个方法都需要抛出异常
解题思路:基础知识
考察点:
分类:{校招,社招}
难度分级:P4
7、IO密集型和CPU密集型与线程池大小的关系
任务性质不同的任务可以用不同规模的线程池分开处理。
CPU密集型任务配置尽可能小的线程,如配置Ncpu+1个线程的线程池。
IO密集型任务则由于线程并不是一直在执行任务,则配置尽可能多的线程,如2*Ncpu。
混合型的任务,如果可以拆分,则将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率,如果这两个任务执行时间相差太大,则没必要进行分解。可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。
参考资料:http://www.infoq.com/cn/articles/java-threadPool
解题思路:基础知识
考察点:
分类:{校招,社招}
难度分级:P4,P5
8、ThreadLocal 作用和实现机制
参考答案:http://wowlinda80.iteye.com/blog/228600
解题思路:基础知识
考察点:
分类:{校招,社招}
难度分级:P4,P5
9、Java线程池corePoolSize,maxPoolSize,queueCapacity这些参数的意义,如何考虑这些参数的设置
corePoolSize(线程池的基本大小):核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。
maximumPoolSize(线程池最大大小):当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maximumPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。
queueCapacity(任务队列容量):从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。
参数的设置参考http://blog.csdn.net/zhouhl_cn/article/details/7392607
解题思路:最好搞清楚线程池的主要处理流程。
考察点:线程池的理解与使用
分类:{校招,社招}
难度分级:P4,P5
10、ThreadLocal是什么,有什么典型的应用场景?
参考答案:http://wowlinda80.iteye.com/blog/228600
解题思路:最好搞清楚线程池的主要处理流程。
考察点:线程池的理解与使用
分类:{校招,社招}
难度分级:P4,P5
11、考虑设计一个计数器程序,场景一,一个线程写,多个线程读;场景二,多个线程写,多个线程读。分别应对这2种场景设计一个计数器程序,要保证线程安全
一写多读的情况主要需要考虑读到最新的数据,所以加volatile关键字即可
多写多读的情况就需要考虑原子操作,可以利用CAS原理
解题思路:基础知识
考察点:多线程,异步
分类:{校招,社招}
难度分级:P4,P5
12、多线程的使用场景,为了解决什么样的问题。不适用于什么场景
这几点阐述的不完全正确
多线程优势
1.发挥多核处理器的强大能力,如果没有多核处理器,用多线程也只是形式而已,提升处理速度,常见的是一个任务用多线程跑比单线程跑快
2.建模简单,可以把一系列的复杂交互,变成简单的同步编程模型;比如在写controller时不用在考虑线程创建、销毁、调度优先级等;如果用单线程模型会变得非常复杂。
3.异步事件的简化处理,参见NIO的实现
多线程的风险
1.线程安全性
2.活跃性(死锁)问题
3.性能问题,频繁切换上下文,调度
想要同时处理多件事:单线程处理不了的,必须使用多线程。(类似于分身术)多个线程分解大任务:用单线程可以做,但是使用多线程可以更快。(类似于左右开弓)可以引申到异步和多线程的区别上。
解题思路:基础知识
考察点:多线程,异步
分类:{校招,社招}
难度分级:P4,P5
13、线程池的实现原理
线程池的作用是有效的降低频繁创建销毁线程所带来的额外开销。一般来说,线程池都是采用预创建的技术,在应用启动之初便预先创建一定数目的线程。应用在运行的过程中,需要时可以从这些线程所组成的线程池里申请分配一个空闲的线程,来执行一定的任务,任务完成后,并不是将线程销毁,而是将它返还给线程池,由线程池自行管理。如果线程池中预先分配的线程已经全部分配完毕,但此时又有新的任务请求,则线程池会动态的创建新的线程去适应这个请求。当然,有可能,某些时段应用并不需要执行很多的任务,导致了线程池中的线程大多处于空闲的状态,为了节省系统资源,线程池就需要动态的销毁其中的一部分空闲线程。因此,线程池都需要一个管理者,按照一定的要求去动态的维护其中线程的数目。线程池将频繁创建和销毁线程所带来的开销分摊到了每个具体执行的任务上,执行的次数越多,则分摊到每个任务上的开销就越小。当然,如果线程创建销毁所带来的开销与线程执行任务的开销相比微不足道,可以忽略不计,则线程池并没有使用的必要。
这个例子不错:
多线程就是通过线程调用线程实现的,打个比方来说就像“摆渡”,河的一岸有很多的人,他们想过河,过河的过程做什么就是他们自己的逻辑,只要他符合我的要求我就送你过河(线程池的要求就是实现Runnable或继承Thread类),然后我开了几条船去送人,只要河的这一岸有满足的人,我就送你过河。这个例子中河一岸的人就是我们要执行的任务,是一个集合,船就是线程池中的线程,由我们自己控制,过河这个动作就是要执行的逻辑,我们只负责把船调给你,怎么划桨怎么过河就是程序自己的逻辑了。
解题思路:基础知识
考察点:线程池
分类:{校招,社招}
难度分级:P4,P5
14、常见的线程工具(同步器、线程安全集合类、原子类),什么场景下使用
解题思路:基础知识
考察点:Java线程状态
分类:{校招,社招}
难度分级:P4
15、常见的线程状态,以及相互转换图;WAITING和BLOCK的区别
线程状态以及相互转换图已有答案。
blocked是内部对象锁阻塞,另外blocked激活需要线程调度器允许其持有对象;BLOCK产生的原因:I/O等待阻塞,suspend挂起等
waiting是等待其他线程通知线程调度器一个条件;WAITING产生原因:Thread.sleep,Object.wait等
解题思路:基础知识
考察点:Java线程状态
分类:{校招,社招}
难度分级:P4
16、常见的同步方式 synchronized 和 lock 如何选择
参考:http://houlinyan.iteye.com/blog/1112535
解题思路:基础知识
考察点:线程同步方式
分类:{校招,社招}
难度分级:P4
17、死锁的几种场景,如何避免死锁
参考答案:参考http://www.cnblogs.com/cxd4321/archive/2012/05/28/2521542.html
解题思路:基础知识
考察点:死锁
分类:{校招,社招}
难度分级:P4
18、利用缓存机制优化一个复杂计算程序
public class Memoizer<A, V> implements Computable<A, V> { private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>(); private final Computable<A, V> c; public Memoizer(Computable<A, V> c) { this.c = c; } public V compute(final A arg) throws InterruptedException { while (true) { } } } Future<V> f = cache.get(arg); if (f == null) { Callable<V> eval = new Callable<V>() { public V call() throws InterruptedException { return c.compute(arg); } }; FutureTask<V> ft = new FutureTask<V>(eval); f = cache.putIfAbsent(arg, ft); if (f == null) { f = ft; ft.run(); } } try { return f.get(); } catch (CancellationException e) { cache.remove(arg, f); } catch (ExecutionException e) { throw launderThrowable(e.getCause()); } |
19、Java方法体中用到了多线程,使用了ThreadPool,但是方法体内程序员没有显式调用ShutDown关闭线程池,那么该方法执行结束后,JVM 是否能自动的关闭线程池,销毁线程?
答案是JVM不能自动关闭线程池,销毁线程。可以参考海丰的CRM每天第一次发布导致cpu load过高报警的问题
解题思路:基础知识
考察点:线程池
分类:{校招,社招都可以用}
难度分级:P4,P5
20、ThreadPool中线程是如何做到重用的?
线程池在执行execute方法的时候,会根据初始化参数的大小以及线程池已有的线程数,来创建核心线程或者把task塞入任务队列;其中创建的核心线程创建后会启动,run方法内会执行一个runWork函数,此函数会不断地从任务队列中获取task执行。
解题思路:线程池的工作原理和工作流程。
考察点:线程池
分类:{校招,社招都可以用}
难度分级:P4,P5