面试最终版
- 1.HashMap与HashTable、ConcurrentHashMap的区别?
- 2.自动锁和手动锁的区别?
- 3.jvm内存模型
- 4.java堆
- 5.什么是分布式
- 6.什么是微服务
- 7.怎么避免XSS攻击
- 8.redis
- 9.gc清理算法
- 10.gc收集器
- 11.什么是并发问题?如何解决?(用户访问高并发)
- 12. 什么是并发问题?如何解决?(多线程同步)
- 13.java项目生产环境上发生死锁怎么解决?
- 14.synchronized和ReentrantLock区别?
- 15.说说mysql事务级别?
- 16.rr级别怎么实现的?
- 17.mysql事务四大特性?
- 18.经常碰到的异常?
- 19.项目中遇到了那些难题,如何解决?
- 20.多线程间的通讯?
- 21.索引原理
- 22.sql优化
- 23.并发容器的使用场景?
- 24.数据库innodb和myisam的区别
- 25.innodb聚集索引的实现
- 26.有用过什么设计模式吗?有没具体的应用场景?
- 附加
- 7.文件内容比较
1.HashMap与HashTable、ConcurrentHashMap的区别?
安全性:HashMap是线程不安全的,HashTable与ConcurrentHashMap是线程安全的。
实现上:HashMap在java7中是使用“数组+链表”,在java8中是使用“数组+链表+红黑树”。
HashTable在是使用“数组+链表”。
ConcurrentHashMap在java7中采用的是“segment数组+entry数组+链表”,java8中则改用“数组+链表+红黑树”。
初始值与扩容:
- HashMap初始值为16,负载因子0.75,扩容为原来 2n(why?)
- HashTable初始值为11,负载因子为0.75,扩容为 2n+1
- ConcurrentHashMap(java1.7)初始值Segments数组长度为16(16个线程同时访问,不可扩),每个Segments[i]默认大小为2,负载因子为0.75,扩容为2n。
Hash来确定位置:
2.自动锁和手动锁的区别?
个人理解:自动锁为内置锁Synchronized,手动锁为显式锁Reentrantlock/Lock。
所以:
- 内置锁不需要显式的获取和释放,显式锁则要(常在finally中)。
- Lock是一个接口,Synchronize是一个关键字。
- Lock可中断,Synchronize不可中断。
- Lock可以提高读的效率(读写锁)。
- Lock可以为公平锁或者非公平锁,Synchronize只能为非公平锁。(why)
3.jvm内存模型
jvm运行时数据区域可以分为两个部分:
- 第一,线程共享的,包括 堆、方法区
- 第二,线程独有的,包括虚拟机栈、本地方法栈、程序计数器
4.java堆
堆可以分为年轻代和年老代,比例为1:2,年轻代又可分为Eden区和Survivor From区和Survivor To,默认比例为8:1:1
5.什么是分布式
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。
首先需要明确的是,只有当单个节点的处理能力无法满足日益增长的计算、存储任务的时候,且硬件的提升(加内存、加磁盘、使用更好的CPU)高昂到得不偿失的时候,应用程序也不能进一步优化的时候,我们才需要考虑分布式系统。因为,分布式系统要解决的问题本身就是和单机系统一样的,而由于分布式系统多节点、通过网络通信的拓扑结构,会引入很多单机系统没有的问题,为了解决这些问题又会引入更多的机制、协议,带来更多的问题。。。
分布式服务顾名思义服务是分散部署在不同的机器上的,一个服务可能负责几个功能,是一种面向SOA架构的,服务之间也是通过rpc来交互或者是webservice来交互的。逻辑架构设计完后就该做物理架构设计,系统应用部署在超过一台服务器或虚拟机上,且各分开部署的部分彼此通过各种通讯协议交互信息,就可算作分布式部署,生产环境下的微服务肯定是分布式部署的,分布式部署的应用不一定是微服务架构的,比如集群部署,它是把相同应用复制到不同服务器上,但是逻辑功能上还是单体应用。
6.什么是微服务
微服务,又叫微服务架构,是一种软件架构方式。它将应用构建成一系列按业务领域划分模块的、小的自治服务。
在微服务架构中,每个服务都是自我包含的,并且实现了单一的业务功能。
可扩展,单独部署,出问题不导致奔溃等
微服务帮助开发者提高效率
简单, 风险更小的部署
7.怎么避免XSS攻击
- HttpOnly防止劫取Cookie
- 输入检查
- 输出检查
- 处理富文本
8.redis
https://www.jianshu.com/p/36a646cef11a
9.gc清理算法
- 标记-清除:标记不可达对象,清除该对象,会产生内存碎片
- 标记-整理:标记不可达对象,清除后将存活对象移动为连续内存空间,不会产生内存碎片,但耗费资源
- 复制:将内存分为相等的两块,对有存活对象的那块内存中的存活对象进行复制到另一块内存,清空原内存,耗费内存
- 分代:将内存分为年轻代和年老代,对不同区域使用不同的算法
10.gc收集器
Serial收集器-》Parallel收集器-》CMS-》G1,用户线程停顿时间不断缩短,但仍然无法完全消除
工作在年轻代的有:
- Serial收集器:复制算法,单线程,简单高效(没有线程切换开销),client端默认
- ParNew收集器:复制算法,多线程,一般用在Server端,配合CMS收集器
- Paraller Scavenge收集器:复制算法,多线程,注重吞吐率,Server默认
工作在年老代的有: - Serial Old收集器:标记-整理算法,单线程,Server模式下配合Parallel Scavenge使用及做CMS后备
- Parallel Old收集器:标记-整理算吧,多线程,注重吞吐率,配合Parallel Scavenge
- CMS收集器:标记-清除算法,多线程并发,低停顿,有内存碎片,注重停顿时间
- G1收集器:Region划分,多线程并发,可预测
11.什么是并发问题?如何解决?(用户访问高并发)
https://blog.csdn.net/xiao__jia__jia/article/details/81703774
- HTML静态化
- 图片服务器分离
- 数据库优化
- 缓存
- 负载均衡
- 并发控制
- 消息队列
12. 什么是并发问题?如何解决?(多线程同步)
- 同步方法(public synchronized void save(){})
- 同步代码块(synchronized(object){})
- 使用特殊域变量(volatile)实现线程同步(volatile不会提供任何原子操作,它也不能用来修饰fianl类型的变量)
- 使用重入锁实现线程同步(ReentrantLock)
- 使用局部变量实现线程同步(ThreadLocal)
13.java项目生产环境上发生死锁怎么解决?
14.synchronized和ReentrantLock区别?
相似点:加锁式同步、阻塞式同步、可重入
功能区别:
- Synchronized是关键字,语法层面实现,而ReentrantLock为API层面实现
- Synchronized使用方便,自动释放,ReentrantLock需要lock()、unlock()方法配合try/finally语句完成
- Synchronized是非公平锁,而ReentrantLock可以为公平锁或非公平锁
- ReentrantLock提供了等待可中断,Synchronized不可以
- ReentrantLock可以绑定多个条件,指定唤醒线程,而synchronized要么随机唤醒一个线程要么唤醒全部线程
- ReentrantLock粒度和灵活度都优于Synchronize
性能区别: - Synchronized引入了偏向锁、轻量级锁后性能与ReentrantLock相当
15.说说mysql事务级别?
事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted) 是 是 是
不可重复读(read-committed) 否 是 是
可重复读(repeatable-read) 否 否 是
串行化(serializable) 否 否 否
16.rr级别怎么实现的?
https://www.jianshu.com/p/2953c64761aa
17.mysql事务四大特性?
1、原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2、一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
3、隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
4、持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
18.经常碰到的异常?
- 空指针异常类:NullPointerException
- 类型强制转换异常:ClassCastException
- 数组负下标异常:NegativeArrayException
- 数组下标越界异常:ArrayIndexOutOfBoundsException
- 违背安全原则异常:SecturityException
- 当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。
- 也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。
concurrentModifyException - 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
- 1、如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
2、如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
19.项目中遇到了那些难题,如何解决?
20.多线程间的通讯?
1、synchronized 实现内存可见性,满足线程共享变量
2、wait/notify\notifyAll(synchronized同步方法或同步块中使用) 实现内存可见性,及生产消费模式的相互唤醒机制
3、同步锁(Lock)的Condition(await\signal\signalAll)
4、管道,实现数据的共享,满足读写模式
21.索引原理
定义:索引(Index)是帮助MySQL高效获取数据的数据结构
22.sql优化
- 对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
- 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
- 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
- 应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
- in 和 not in 也要慎用,否则会导致全表扫描
- 全表扫描:select id from t where name like ‘%abc%’
- 如果在 where 子句中使用参数,也会导致全表扫描
- 应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描
- select count(*) from table;这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。
- 任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
23.并发容器的使用场景?
常用的juc并发容器:
- HashTable-ConcurrentHashMap
- ConcurrentLinkedQueue使用链表作为数据结构,它采用无锁操作,可以任务是高并发环境下性能最好的队列(非阻塞线程安全队列,无界,故不太适合做生产者消费者模式)
- LinkedBlockingQueue阻塞线程安全队列,可以做到有界,通常用于生产者消费者模式
- CopyOnWriteArrayList提供高效地读取操作,使用在读多写少的场景
- ConcurrentSkipListMapSkipList(跳表)是一种随机性的数据结构,用于替代红黑树,因为它在高并发的情况下,性能优于红黑树。跳表实际上是以空间换取时间
- CountDownLatch(闭锁)
CopyOnWrite容器(写时复制容器)适用于读操作远远多于写操作,并且数据量较小的情况。
24.数据库innodb和myisam的区别
- 事务
- 外键
- 锁
- 全文索引
- 表主键
- 表行数
- 自增主键
25.innodb聚集索引的实现
https://blog.csdn.net/jeffrey11223/article/details/78997941
26.有用过什么设计模式吗?有没具体的应用场景?
- 单例模式:各种连接池,如数据库连接池
- 代理模式:AOP
- 装饰器模式:IO流
- 工厂模式:Spring
附加
1.为什么HashMap在多线程下不安全?
hashmap在多线程下重哈希(resize)会导致get的时候死循环。
2.为什么HashMap扩容为原来2倍,而HashTable为2n+1?
原因是HashMap与HashTable所用的Hash函数有区别
3.为Lock可以为公平锁或者非公平锁,Synchronize只能为非公平锁?
Lock是用过AQS来进行并发控制的,所以可以控制执行的顺序,而Synchronize不可以。
4.CMS收集过程及优缺点
5.G1收集过程及优缺点
6.多线程打印
public class Test2
{
// 测试
public static void main(String[] args) throws Exception
{
Object obj = new Object();
// 启动两个线程
Thread1 t1 = new Thread1(obj);
Thread2 t2 = new Thread2(obj);
t1.start();
t2.start();
}
}
// 一个线程打印1-52
class Thread1 extends Thread
{
private Object obj;
public Thread1(Object obj)
{
this.obj = obj;
}
public void run()
{
synchronized (obj)
{
// 打印1-52
for (int i = 1; i < 53; i++)
{
System.out.print(i + " ");
if (i % 2 == 0)
{
// 不能忘了 唤醒其它线程
obj.notifyAll();
try
{
obj.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
}
// 另一个线程打印字母A-Z
class Thread2 extends Thread
{
private Object obj;
public Thread2(Object obj)
{
this.obj = obj;
}
public void run()
{
synchronized (obj) //同步监视器是obj类,同步代码块是写在run方法里面的。
{
// 打印A-Z
for (int i = 0; i < 26; i++)
{
System.out.print((char)('A' + i) + " ");
// 不能忘了 唤醒其它线程
obj.notifyAll();
try
{
// 最后一个就不要等了
if (i != 25)
{
obj.wait();
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
/**
* 多线程交替打印 1 - 100
* @author Logan
*
*/
public class Test3 {
// 创建变量
int i = 1;
public static void main(String[] args) {
// 创建该类的对象
Test3 obj = new Test3();
// 使用匿名内部类的形式,没创建runnable对象
Runnable runnable = new Runnable() {
@Override
public void run() {
while(obj.i < 1000){
// 上锁当前对象
synchronized(this) {
// 唤醒另一个线程
notify();
System.out.println("Thread " + Thread.currentThread().getName() + " "+ obj.i ++);
try {
// 释放掉锁
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
// 启动多个线程(想创建几个就创建几个)
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
}
7.文件内容比较
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
class FileCompare{
public File file;
public int complicCount;
public String folderName;
public FileCompare(File file,int complicCount,String folderName) {
// TODO Auto-generated constructor stub
this.file = file;
this.complicCount = complicCount;
this.folderName = folderName;
}
}
public class Test{
public static void main (String args[]) {
ArrayList<FileCompare> fiList = traverseFolder("C:\\Users\\isad\\Desktop\\testfolder");
for(int i=0;i<fiList.size();i++) {
for(int j=i+1;j<fiList.size();j++) {
if(isSameFile(fiList.get(i).file, fiList.get(j).file)) {
String AbsolutePath = fiList.get(i).file.getAbsolutePath();
String folderName = AbsolutePath.substring(0,AbsolutePath.lastIndexOf("."));
File folder = new File(folderName);
folder.mkdir();
if(folder.exists()) {
fiList.get(i).folderName = fiList.get(i).file.getName();
fiList.get(j).folderName = fiList.get(j).file.getName();
System.out.println("-----------------------------");
System.out.println(fiList.get(i).file.getName());
System.out.println(fiList.get(j).file.getName());
System.out.println("-----------------------------");
}
}
}
}
}
public static ArrayList<FileCompare> traverseFolder(String path) {
ArrayList<FileCompare> listfile = new ArrayList<FileCompare>();
File file = new File(path);
if (file.exists()) {
for (File file2 : file.listFiles()) {
if (!file2.isDirectory()) {
listfile.add(new FileCompare(file2, 0, ""));
}
}
} else {
System.out.println("文件不存在!");
}
return listfile;
}
/**
* 判断两个文件的内容是否相同,文件名要用绝对路径
* @param fileName1 :文件1的绝对路径
* @param fileName2 :文件2的绝对路径
* @return 相同返回true,不相同返回false
*/
public static boolean isSameFile(File fileName1,File fileName2){
FileInputStream fis1 = null;
FileInputStream fis2 = null;
try {
fis1 = new FileInputStream(fileName1);
fis2 = new FileInputStream(fileName2);
int len1 = fis1.available();//返回总的字节数
int len2 = fis2.available();
if (len1 == len2) {//长度相同,则比较具体内容
//建立两个字节缓冲区
byte[] data1 = new byte[len1];
byte[] data2 = new byte[len2];
//分别将两个文件的内容读入缓冲区
fis1.read(data1);
fis2.read(data2);
//依次比较文件中的每一个字节
for (int i=0; i<len1; i++) {
//只要有一个字节不同,两个文件就不一样
if (data1[i] != data2[i]) {
System.out.println("文件内容不一样");
return false;
}
}
System.out.println("两个文件完全相同");
return true;
} else {
//长度不一样,文件肯定不同
return false;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {//关闭文件流,防止内存泄漏
if (fis1 != null) {
try {
fis1.close();
} catch (IOException e) {
//忽略
e.printStackTrace();
}
}
if (fis2 != null) {
try {
fis2.close();
} catch (IOException e) {
//忽略
e.printStackTrace();
}
}
}
return false;
}
}