某Java大佬在地表最强Java企业(阿里)面试总结

2021年11月24日 阅读数:7
这篇文章主要向大家介绍某Java大佬在地表最强Java企业(阿里)面试总结,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

面试题真的是博大精深,也经过这个面试题学到了不少东西,不少笔者也不是很懂,若有描述错误的地方还望大佬赐教,
每一次面试均可能问到相同的问题,一面问到,二三面还可能会问到,笔者认为这一点是整理这篇面试题收获最大的一点。java

目录:mysql

一面

1.一、HashMap和Hashtable的区别
1.二、实现一个保证迭代顺序的HashMap
1.三、 说一说排序算法,稳定性,复杂度
1.四、 说一说GC
1.五、 能够保证的实习时长
1.六、 职业规划面试

二面

2.一、 自我介绍。
2.二、 JVM如何加载一个类的过程,双亲委派模型中有哪些方法?
2.三、 HashMap如何实现的?
2.四、 HashMap和Concurrent HashMap区别, Concurrent HashMap 线程安全吗, Concurrent HashMap如何保证 线程安全?
2.五、 HashMap和HashTable 区别,HashTable线程安全吗?
2.六、 进程间通讯有哪几种方式?
2.七、 JVM分为哪些区,每个区干嘛的?
2.八、 JVM如何GC,新生代,老年代,持久代,都存储哪些东西?
2.九、 GC用的引用可达性分析算法中,哪些对象可做为GC Roots对象?
2.十、 快速排序,过程,复杂度?
2.十一、 什么是二叉平衡树,如何插入节点,删除节点,说出关键步骤。
2.十二、 TCP如何保证可靠传输?三次握手过程?
2.1三、 TCP和UDP区别?
2.1四、 滑动窗口算法?
2.1五、 Linux下如何进行进程调度的?
2.1六、 Linux下你经常使用的命令有哪些?
2.1七、 操做系统什么状况下会死锁?
2.1八、 经常使用的hash算法有哪些?
2.1九、 什么是一致性哈希?
2.20、 如何理解分布式锁?
2.2一、 数据库中的范式有哪些?
2.2二、 数据库中的索引的结构?什么状况下适合建索引?
2.2三、 Java中的NIO,BIO,AIO分别是什么?
2.2四、 用什么工具调试程序?JConsole,用过吗?
2.2五、 如今JVM中有一个线程挂起了,如何用工具查出缘由?
2.2六、 线程同步与阻塞的关系?同步必定阻塞吗?阻塞必定同步吗?
2.2七、 同步和异步有什么区别?
2.2八、 线程池用过吗?
2.2九、 如何建立单例模式?说了双重检查,他说不是线程安全的。如何高效的建立一个线程安全的单例?
2.30、 concurrent包下面,都用过什么?
2.3一、 经常使用的数据库有哪些?redis用过吗?
2.3二、 了解hadoop吗?说说hadoop的组件有哪些?说下mapreduce编程模型。
2.3三、 你知道的开源协议有哪些?
2.3四、 你知道的开源软件有哪些?
2.3五、 你最近在看的书有哪些?
2.3六、 你有什么问题要问我吗?
2.3七、 了解哪些设计模式?说说都用过哪些设计模式
2.3八、 如何判断一个单链表是否有环?
2.3九、 操做系统如何进行分页调度?
2.40、 匿名内部类是什么?如何访问在其外面定义的变量?redis

三面

3.一、 自我介绍,作过什么项目。
3.二、java虚拟机的区域如何划分,
3.三、 双亲委派模型中,从顶层到底层,都是哪些类加载器,分别加载哪些类?
3.四、 有没有可能父类加载器和子类加载器,加载同一个类?若是加载同一个类,该使用哪个类?
3.五、 HashMap的结构,get(),put()是如何实现的?
3.六、 ConcurrentHashMap的get(),put(),又是如何实现的?ConcurrentHashMap有哪些问题? ConcurrentHashMap的锁是读锁仍是写锁?
3.七、 sleep()和wait()分别是哪一个类的方法,有什么区别?synchronized底层如何实现的?用在代码块和方法上有什么区别?
3.八、 什么是线程池?若是让你设计一个动态大小的线程池,如何设计,应该有哪些方法?
3.九、 什么是死锁?JVM线程死锁,你该如何判断是由于什么?若是用VisualVM,dump线程信息出来,会有哪些信息?
3.十、 查看jvm虚拟机里面堆、线程的信息,你用过什么命令?
3.十一、 垃圾回收算法有哪些?CMS知道吗?如何工做的?
3.十二、 数据库中什么是事务?事务的隔离级别?事务的四个特性?什么是脏读,幻读,不可重复读?
3.1三、 数据库索引的结构有哪些? 介绍B+树的结构。
3.1四、 数据库中的分页查询语句怎么写?
3.1五、 什么是一致性哈希?用来解决什么问题?
3.1六、 Redis的存储结构,或者说如何工做的,与mysql的区别?有哪些数据类型?
3.1七、 项目中用到redis,为何选用redis,了解其余NoSQL数据库吗?在你的项目中是如何运用redis的?key是什么,value是什么?
3.1八、 归并排序的过程?时间复杂度?空间复杂度?你日常用什么排序?快速排序。说说在那些场景下适用,哪些场景下不适用。
3.1九、 Solr是如何工做的?算法

一面

<div id="1_1"></div>sql

1.一、HashMap和Hashtable的区别

继承:
Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但两者都实现了Map接口。数据库

锁:
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省状况下是非Synchronize的。编程

方法:
HashMap把Hashtable的contains方法去掉了,改为containsValue和containsKey,由于contains方法容易让人引发误解。
Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。设计模式

是否能够为null:
Hashtable中,key和value都不容许出现null值。
HashMap中,null能够做为键,这样的键只有一个。
Hashtable中有相似put(null,null)的操做,编译一样能够经过,由于key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。
Tips:
当get()方法返回null值时,多是 HashMap中没有该键,也可能使该键所对应的值为null。所以,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。数组

遍历:
Hashtable、HashMap都使用了 Iterator。可是,Hashtable还使用了Enumeration的方式 。
计算Hash值:
HashTable直接使用对象的hashCode。
HashMap的Hash值:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
容量:
HashTable在不指定容量的状况下的默认容量为11,而HashMap为16,
Hashtable不要求底层数组的容量必定要为2的整数次幂,而HashMap则要求必定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

<div id="1_2"></div>

1.二、实现一个保证迭代顺序的HashMap

使用HashMap的一个子类LinkedHashMap(顺序遍历的HashMap)进行研究

放入方法中:
重写了最关键的Node<K, V> newNode(int hash, K key, V value, Node next)方法,该方法首先建立一个HashMap.Node的子类LinkedHashMap.Entry,它比Node多了两个指针Entry<K, V> before, after;//用于保存先后两个节点的信息。

Tips:
LinkedHashMap拥有两个瞬时的属性
transient LinkedHashMap.Entry&lt;K,V&gt; tail;//用于保存上一个元素,即表尾;
transient LinkedHashMap.Entry&lt;K, V&gt; head;//用于保存第一个元素,即表头。

遍历:
使用LinkedEntryIterator迭代器进行遍历,继承于 abstract class LinkedHashIterator抽象类。该迭代器拥有两个Entry的指针nextcurrent,并结合Entry中的beforeafter指针,实现了LinkedHashMap中元素的顺序遍历

LinkedHashIterator的nextNode方法使用了LinkedHashMap.Entry&lt;K, V&gt;的after属性,使得iterator的遍历按照放入顺序进行的。

取值方法:
LinkedHashMap重写了Map接口的V get(Object key)方法,该方法分两个步骤:

  1. 调用父类HashMap的getNode(hash(key), key)方法,获取value;
  2. 若是accessOrder(访问后重排序)为true(默认为false),那么移动所访问的元素到表尾,并修改head和tail的值。

<div id="1_3"></div>

1.三、 说一说排序算法,稳定性,复杂度

在这里插入图片描述
这个东西仍是面试前把每一个排序算法都看一看比较好

<div id="1_3"></div>

1.四、 说一说GC

堆(新生代和老生代)是Java虚拟机进行垃圾回收的主要场所,其次要场所是方法区(永久代)
在堆中进行垃圾回收分为新生代和老生代;将新生代分红了三个独立的区域(这里的独立区域只是一个相对的概念,并非说分红三个区域之后就再也不互相联合工做了),
分别为:Eden区、From Survivor区以及To Survivor,而Eden区分配的内存较大,其余两个区较小,每次使用Eden和其中一块Survivor。

在进行垃圾回收时,将Eden和Survivor中还存活着的对象进行一次性地复制到另外一块Survivor空间上,直到其两个区域中对象被回收完成,
当Survivor空间不够用时,须要依赖其余老年代的内存进行分配担保。当另一块Survivor中没有足够的空间存放上一次新生代收集下来的存活对象时,这些对象将直接经过分配担保机制进入老生代,大对象和长期存活的对象也会直接进入老年代。
若是老生代的空间也被占满,当来自新生代的对象再次请求进入老生代时就会报OutOfMemory异常。

新生代中的垃圾回收频率高, ,保存在JVM的方法区(永久代)中的对象通常不会被回收。
其永久代进行垃圾回收的频率就较低,速度也较慢。
永久代的垃圾收集主要回收废弃常量和无用类。 
判断一个类是否被回收,则需同时知足的条件: 
该类全部的实例和ClassLoader都已经被回收。
该类的对象没有被引用,没法经过反射访问,这里说的是能够回收而不是必然回收。

大多数状况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC;
同理,当老年代中没有足够的内存空间来存放对象时,虚拟机会发起一次Major GC/Full GC。只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,不然将进行Full CG。

虚拟机经过对象年龄计数器来判断存放在哪:
若是对象在Eden出生并通过一次Minor GC后仍然存活,而且能被Survivor容纳的话,将被移动到Survivor空间中,并将该对象的年龄设为1。
对象每在Survivor中熬过一次Minor GC,年龄就增长1岁,当他的年龄增长到最大值15(MaxTenuringThreshold)时,就将会被晋升到老年代中。
若是在Survivor空间中全部相同年龄的对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就能够直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。

Jdk8开始废弃永久代:
This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.
(移除永久代是为融合HotSpot JVM与 JRockit VM而作出的努力,由于JRockit没有永久代,不须要配置永久代。)
因为永久代内存常常不够用或发生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen
字符串存在永久代中,容易出现性能问题和内存溢出。
类及方法的信息等比较难肯定其大小,所以对于永久代的大小指定比较困难,过小容易出现永久代溢出,太大则容易致使老年代溢出。
永久代会为GC带来没必要要的复杂度,并且回收效率偏低。

<div id="1_5"></div>

1.五、 能够保证的实习时长

通常都是半年到一年,(太少的话,刚教会了你,你就可能走了,太多的话也不现实)
还有可能问到何时去上班,建议的话,就是下个月,或者两周后,今天面试明天上班,确定准备的不充分。(问这个的话,大多都是有个项目什么的着急要人,而后面试官用你应付,)

<div id="1_6"></div>

1.六、 职业规划

其实这个问题不仅是回答面试官,更是回答本身,为何作,想怎么作……

说明本身对岗位的理解和从事这份工做的缘由。
说明本身愿意为这份工做付出努力。
说明本身长远的目标和规划。

下面以产品经理为例子回答:
互联网行业是一个高速发展的行业,同时也有大量创新和尝试的机会(阐述本身看好行业)。
而产品经理则是互联网企业的核心岗位之一,产品经理负责用户需求分析、竞品分析、产品设计和上下层需求的沟通,须要超强的逻辑思考和分析能力、用户洞察能力和沟通协做能力。(阐述本身对岗位的理解)。
而我毕业于XXX大学,在大学里曾参加XXX产品设计比赛,拿下了XXX的成绩,我的很是擅长思考和分析问题,同时能处理好和团队成员的沟通协做...(阐述本身适合这个工做)。

我认为本身很是适合这个岗位,为此,我也愿意付出努力。
在过去,我曾阅读过XXX本产品书籍,本身设计过3款产品的原型,有一款在本身的努力下成功上线,并经过持续获取用户反馈,收获了XXX万的用户。(表达本身过去的努力)
入职之后,我但愿能从助理开始,系统学习产品的基本功,在一年的时间里面,成功掌握主流的产品设计方法论(表达本身愿意付出的努力)。
我知道,优秀的产品经理不只仅须要掌握产品设计的方法,还须要XXXX。
我会努力培养本身的业务思惟,站在全局业务的角度去思考和解决问题,为团队作好表率...(表达本身大体的努力方向)。

每一个产品经理都有本身的目标,我也同样。
我但愿在个人努力之下,在两年之后,可以独挡一面,负责好一个版块的功能;
在三到五年左右,能够负责好一个产品的规划、设计和优化;
在将来的五到八年,能够作好一个产品的全局规划、团队管理等等....

二面

<div id="2_1"></div>

2.一、 自我介绍。

自我介绍在三到五分钟最好
一两句话归纳本身名字学校什么的,主学的什么,对什么有研究,了解什么(切忌:尽可能别说“精通”),而后说一下之前作过的项目(具体说一些本身作的有技术的),或者什么别的奖项,而后谈一下本身对公司的了解以及对将来的规划,等等。(百度有不少,随便搜搜)

<div id="2_2"></div>

2.二、 JVM如何加载一个类的过程,双亲委派模型中有哪些方法?

类加载过程:
加载
(经过一个类的全限定名获取定义此类的二进制字节流,将这个字节流所表明的静态存储结构转化为方法区域的运行时数据结构,在Java堆中生成一个表明这个类的java.lang.Class对象,做为方法区域数据的访问入口)、
验证
(验证阶段做用是保证Class文件的字节流包含的信息符合JVM规范,不会给JVM形成危害,若是验证失败,就会抛出一个java.lang.VerifyError异常或其子类异常。
1.文件格式验证:
验证字节流文件是否符合Class文件格式的规范,而且能被当前虚拟机正确的处理。
2.元数据验证:
是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范。
3.字节码验证:
主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。
4.符号引用验证:
符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动做将在解析阶段中发生。)、
准备
(准备阶段为(static)变量(不包括类的实例)分配内存并设置类变量的初始化,此初始化并非赋值static int num =1,这时得num为0,并非1)、
解析
(解析过程是将常量池内的符号引用替换成直接引用(类或接口的解析、字段解析、方法解析、接口方法解析。))、
初始化
(这里才是赋值阶段)
使用过程:新线程---程序计数器----jvm栈执行(对象引用)-----堆内存(直接引用)----方法区。
卸载靠GC

双亲委派模型中方法:
双亲委派是指若是一个类收到了类加载的请求,不会本身先尝试加载,先找父类加载器去完成。当顶层启动类加载器表示没法加载这个类的时候,子类才会尝试本身去加载。当回到最开的发起者加载器还没法加载时,并不会向下找,而是抛出ClassNotFound异常。

方法:启动(Bootstrap)类加载器,标准扩展(Extension)类加载器,应用程序类加载器(Application ),上下文(Custom)类加载器。意义是防止内存中出现多份一样的字节码 。

1)启动类加载器(Bootstrap ClassLoader):
负责加载JAVA_HOME\lib目录中而且能被虚拟机识别的类库到JVM内存中,若是名称不符合的类库即便放在lib目录中也不会被加载。该类加载器没法被Java程序直接引用。
2)扩展类加载器(Extension ClassLoader):
按《深刻理解java虚拟机》这本书上所说,该加载器主要是负责加载JAVA_HOME\lib\ext目录中的类库,可是貌似在JDK的安装目录下,没看到该指定的目录。该加载器能够被开发者直接使用。
3)应用程序类加载器(Application ClassLoader):
该类加载器也称为系统类加载器,它负责加载用户类路径(Classpath)上所指定的类库,开发者能够直接使用该类加载器,若是应用程序中没有自定义过本身的类加载器,通常状况下这个就是程序中默认的类加载器。

<div id="2_3"></div>

2.三、 HashMap如何实现的?

关于HashMap仍是看一下这一篇比较好

HashMap的底层是数组+链表,(不少人应该都知道了)

JDK1.7的是数组+链表
首先是一个数组,而后数组的类型是链表
元素是头插法
JDK1.8的是数组+链表 或者 数组+红黑树
首先是一个数组,而后数组的类型是链表

在链表的元素大于8的时候,会变成红黑树
(当链表长度大于8而且数组长度大于64时,才会转换为红黑树。
若是链表长度大于8,可是数组长度小于64时,仍是会进行扩容操做,不会转换为红黑树。由于数组的长度较小,应该尽可能避开红黑树。由于红黑树须要进行左旋,右旋,变色操做来保持平衡,
因此当数组长度小于64,使用数组加链表比使用红黑树查询速度要更快、效率要更高。 )

在红黑树的元素小于6的时候会变成链表
(这里须要注意,不是元素小于6的时候必定会变成链表,只有resize的时候才会根据UNTREEIFY_THRESHOLD 进行转换,一样也不是到8的时候就变成红黑树(不是等到扩容的时候) 链表与红黑树的转换详情)
元素进行尾插

<div id="2_4"></div>

2.四、 HashMap和Concurrent HashMap区别, Concurrent HashMap 线程安全吗, Concurrent HashMap如何保证 线程安全?

关于HashMap仍是看一下这一篇比较好

使用ConcurrentHashMap(线程安全),
JDK1.7的是分段数组,有Segment锁(继承于ReentrantLock)加速一小段保证并发
JDK1.8 是和HashMap同样了,数组+链表(或者红黑树)
Synchronized(锁)and CAS(compare and swap)
(JVM在1.6对Synchronize的优化很好)

CAS通俗易懂,比较并替换
(CAS是一种无锁算法,CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作)
(无锁化的修改值的操做,他能够大大下降锁代理的性能消耗。这个算法的基本思想就是不断地去比较当前内存中的变量值与你指定的 一个变量值是否相等,若是相等,则接受你指定的修改的值,不然拒绝你的操做。由于当前线程中的值已经不是最新的值,你的修改极可能会覆盖掉其余线程修改的结果。这一点与乐观锁,SVN的思想是比较相似的)

<div id="2_5"></div>

2.五、 HashMap和HashTable 区别,HashTable线程安全吗?

1.一、HashMap和Hashtable的区别
HashTable(线程安全)就是把HashMap套上了一个Synchronized

<div id="2_6"></div>

2.六、 进程间通讯有哪几种方式?

管道、消息队列、信号量、共享内存、套接字
无名管道( pipe ):
管道是一种半双工的通讯方式,数据只能单向流动,并且只能在具备亲缘关系的进程间使用。进程的亲缘关系一般是指父子进程关系。

高级管道(popen):
将另外一个程序当作一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式咱们成为高级管道方式。

有名管道 (named pipe) :
有名管道也是半双工的通讯方式,可是它容许无亲缘关系进程间的通讯。

消息队列( message queue ) :
消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

信号量( semophore ) :
信号量是一个计数器,能够用来控制多个进程对共享资源的访问。它常做为一种锁机制,防止某进程正在访问共享资源时,其余进程也访问该资源。所以,主要做为进程间以及同一进程内不一样线程之间的同步手段。

信号 ( sinal ) :
信号是一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生。

共享内存( shared memory ) :
共享内存就是映射一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问。共享内存是最快的 IPC 方式,它是针对其余进程间通讯方式运行效率低而专门设计的。它每每与其余通讯机制,如信号两,配合使用,来实现进程间的同步和通讯。

套接字( socket ) :
套解口也是一种进程间通讯机制,与其余通讯机制不一样的是,它可用于不一样机器间的进程通讯。

<div id="2_7"></div>

2.七、 JVM分为哪些区,每个区干嘛的?

线程独占 : 栈 , 本地方法栈 ,程序计数器
线程共享 : 堆 , 方法区

程序计数器PC
线程私有的
它能够看作是当前线程所执行的字节码的行号指示器
内存区域中惟一一个没有规定任何OutOfMemoryError的区域

Java虚拟机栈
线程私有的
每一个方法在执行的同时都会建立一个栈帧,用于存储局部变量表、操做数栈、动态连接、方法出口等信息
若是线程请求的栈深度大于虚拟机所容许的深度,将抛StackOverFlowError异常;
如虚拟机扩展时仍没法申请到足够的内存,就会抛出OutOfMemoryError异常

本地方法栈
与虚拟机栈很是类似,区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用Native方法服务
也会抛出StackOverFlowError和OutOfMemoryError异常

Java堆
线程共享的
Java堆是GC管理的主要区域
在虚拟机启动时建立
存放对象实例,几乎全部的对象实例和数组都在这里分配内存。
若是在堆中没有内存完成实例分配,而且堆也没法再扩展时,将会抛出OutOfMemoryError异常

方法区
线程共享的
用于存储已被虚拟机加载的类信息、常量、静态变量、即便编译器编译后的代码等数据
当方法区没法知足内存分配需求时,将抛出OutOfMemoryError异常

运行时常量池
是方法区的一部分
用于存放编译器生成的各类字面量和符号引用
相对于Class文件常量池的一个重要特征是,具有动态性
运行时常量池是方法区的一部分,天然受到方法区内存的限制。当常量池没法再申请到内存时会抛出OutOfMemoryError异常

<div id="2_8"></div>

2.八、 JVM如何GC,新生代,老年代,持久代,都存储哪些东西?

1.四、 说一说GC

<div id="2_9"></div>

2.九、 GC用的引用可达性分析算法中,哪些对象可做为GC Roots对象?

可达性分析算法的思想:
从一个被称为GC Roots的对象开始向下搜索,若是一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。
在java中能够做为GC Roots的对象有如下几种:
虚拟机栈中引用的对象、方法区类静态属性引用的对象、方法区常量池引用的对象、本地方法栈JNI引用的对象

虽然这些算法能够断定一个对象是否能被回收,可是当知足上述条件时,一个对象 不必定会被回收。当一个对象不可达GC Roots时,这个对象并不会立刻被回收,而是处于一个死缓的阶段,若要被真正的回收须要经历两次标记。若是对象在可达性分析中没有与GC Roots的引用链,那么此时就会被第一次标记而且进行一次筛选,筛选的条件是是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者已经被虚拟机调用过,那么就认为是不必的。

若是该对象有必要执行finalize()方法,那么这个对象将会放在一个称为F-Queue的队列中,虚拟机会触发一个finalize()线程去执行,此线程是低优先级的,而且虚拟机不会承诺一直等待它运行完,这仍是由于若是finalize()执行缓慢或者发生了死锁,那么就会形成F-Queue队列一直等待,形成了内存回收系统的崩溃。GC对处于F-Queue中的对象进行第二次被标记,这时,该对象将被移除“即将回收”集合,等待回收。

<div id="2_10"></div>

2.十、 快速排序,过程,复杂度?

//快速排序
void quick_sort(int s[], int l, int r)
{
    if (l < r)
    {
        //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
        int i = l, j = r, x = s[l];
        while (i < j)
        {
            while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
                j--;  
            if(i < j) 
                s[i++] = s[j];

            while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
                i++;  
            if(i < j) 
                s[j--] = s[i];
        }
        s[i] = x;
        quick_sort(s, l, i - 1); // 递归调用 
        quick_sort(s, i + 1, r);
    }
}

快速排序,分治递归,对于每一段作以下处理:
从右向左第一个小于x的数放在原来左位置+1得那个地方,相反,从左向右第一个大于x的数放到原来右位置-1,一直到坐位置》=右位置,而后中间位置=原来左面的那个位置的数,在递归调用(l,i-1)和(i+1,r)

<div id="2_11"></div>

2.十一、 什么是二叉平衡树,如何插入节点,删除节点,说出关键步骤。

对于每个结点来讲,子树和右子树都是二叉平衡树,左右子树的高度差不能大于1,若是插入删除使得高度差大于1了,就要进行旋转操做
插入:
若是有当前结点就返回false,就插入,若是仍是二叉平衡树就返回true,插入的过程当中若是不符合条件,就左旋右旋处理
删除:
(1)删除节点没有左子树,这种状况直接将删除节点的父节点指向删除节点的右子树。
(2)删除节点没有右子树,这种状况直接将删除节点的父节点指向删除节点的左子树。
(3)删除节点左右子树都存在,能够采用两种方式,
1:让删除节点左子树的最右侧节点代替当前节点
2:让删除节点右子树的最左侧节点代替当前节点

<div id="2_12"></div>

2.十二、 TCP如何保证可靠传输?三次握手过程?

TCP为了提供可靠传输:
(1)首先,采用三次握手来创建TCP链接,四次握手来释放TCP链接,从而保证创建的传输信道是可靠的。
(2)其次,TCP采用了连续ARQ协议(回退N,Go-back-N;超时自动重传)(自动重传请求(Automatic Repeat-reQuest,ARQ))来保证数据传输的正确性,使用滑动窗口协议来保证接方可以及时处理所接收到的数据,进行流量控制。
(3)最后,TCP使用慢开始、拥塞避免、快重传和快恢复来进行拥塞控制,避免网络拥塞。

在TCP/IP协议中,TCP协议提供可靠的链接服务,采用三次握手创建一个链接。
第一次握手: 创建链接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时本身也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据.

<div id="2_13"></div>

2.1三、 TCP和UDP区别?

TCP与UDP区别总结
一、TCP面向链接(如打电话要先拨号创建链接);UDP是无链接的,即发送数据以前不须要创建链接
二、TCP提供可靠的服务。也就是说,经过TCP链接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。TCP经过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还能够对次序乱掉的分包进行顺序控制。
三、UDP具备较好的实时性,工做效率比TCP高,适用于对高速传输和实时性有较高的通讯或广播通讯。
4.每一条TCP链接只能是点到点的;UDP支持一对1、一对多、多对一和多对多的交互通讯。
五、TCP对系统资源要求较多,UDP对系统资源要求较少。
为何UDP有时比TCP更有优点?
UDP以其简单、传输快的优点,在愈来愈多场景下取代了TCP,如实时游戏。
(1)网速的提高给UDP的稳定性提供可靠网络保障,丢包率很低,若是使用应用层重传,可以确保传输的可靠性。
(2)TCP为了实现网络通讯的可靠性,使用了复杂的拥塞控制算法,创建了繁琐的握手过程,因为TCP内置的系统协议栈中,极难对其进行改进。
采用TCP,一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接收到后再继续发送,延时会愈来愈大,基于UDP对实时性要求较为严格的状况下,采用自定义重传机制,可以把丢包产生的延迟降到最低,尽可能减小网络问题对游戏性形成影响。

<div id="2_14"></div>

2.1四、 滑动窗口算法?

大概意思:在一个数组或者其余链表中,确认左端点和右端点,这中间用和或者其余的存起来,一个一个的向右移动右端点,这个过程当中可能因不符合条件要把左端点也右移,在这个过程当中一直记录最大值或者最小值,(向右移动左端点就是在这个范围的和或者其余记录的数值删去左端点这个值)

<div id="2_15"></div>

2.1五、 Linux下如何进行进程调度的?

凉凉…………(这个不会,看也看不懂的那种)
1.先来先服务调度算法
  先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于做业调度,也可用于进程调度。当在做业调度中采用该算法时,
每次调度都是从后备做业队列中选择一个或多个最早进入该队列的做业,将它们调入内存,为它们分配资源、建立进程,而后放入就绪
队列。在进程调度中采用FCFS算法时,则每次调度是从就绪队列中选择一个最早进入该队列的进程,为之分配处理机,使之投入运行。
该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。
二、基于优先级调度 (Priority Scheduling)
  在优先级调度算法中,每一个进程都关联一个优先级,内核将CPU分配给最高优先级的进程。具备相同优先级的进程,按照
先来先服务的原则进行调度。
Aging就是指逐渐提升系统中长时间等待的进程的
优先级。咱们能够每15分钟将等待进程的优先级加1。最终
通过一段时间,即使是拥有最低优先级的进程也会变成系统中最高优先级的进程,从而被执行。

  优先级调度能够抢占式或者非抢占式的。当一个进程在Ready队列中时,内核将它的优先级与正在CPU上执行的进程的优先级
进行比较。当发现这个新进程的优先级比正在执行的进程高时:对于抢占式内核,新进程会抢占CPU,以前正在执行的进程
转入Ready队列;对于非抢占式内核,新进程只会被放置在Ready队列的头部,不会抢占正在执行的进程。
三、短进程优先(SCBF--Shortest CPU Burst First)
  最短CPU运行期优先调度算法(SCBF--Shortest CPU Burst First)
该算法从就绪队列中选出下一个“CPU执行期最短”的进程,为之分配处理机。
最短做业优先调度是优先级调度的特例。在优先级调度中咱们根据进程的优先级来进行调度,在最短做业优先调度中咱们
根据做业的执行时间长短来调度。
四、轮转法 (Round-Robin Scheduling) (RR)
  前几种算法主要用于批处理系统中,不能做为分时系统中的主调度算法,在分时系统中,都采用时间片轮转法。
简单轮转法:系统将全部就绪进程按FIFO规则排队,按必定的时间间隔把处理机分配给队列中的进程。这样,就绪
队列中全部进程都可得到一个时间片的处理机而运行。多级队列方法:将系统中全部进程分红若干类,每类为一级。
  RR调度算法转为分时系统设计,它与FCFS很像,可是加入了抢占。具体调度过程是:内核从Ready队列中选取第一个进程,
将CPU资源分配给它,而且设置一个定时器在一个时间片后中断该进程,调度Ready队列中的下一进程。很明显,RR调度
算法是抢占式的,而且在该算法的调度下,没有一个进程可以连续占用CPU超过一个时间片,从而达到了分时的目的。
五、高响应比优先调度算法
  (1) 若是做业的等待时间相同,则要求服务的时间愈短,其优先权愈高,于是该算法有利于短做业.
  (2) 当要求服务的时间相同时,做业的优先权决定于其等待时间,等待时间愈长,其优先权愈高,于是它实现的是先来先服务.
  (3) 对于长做业,做业的优先级能够随等待时间的增长而提升,当其等待时间足够长时,其优先级即可升到很高, 从而也可得到处理机.
  该算法照顾了短做业,且不会使长做业长期得不到服务
六、抢占式调度算法

  1. 非抢占式调度算法
    为每个被控对象创建一个实时任务并将它们排列成一轮转队列,调度程序每次选择队列中的第一个任务投入运行.该任务完成后便把它挂在轮转队列的队尾等待下次调度运行.
  2. 非抢占式优先调度算法.
    实时任务到达时,把他们安排在就绪队列的对首,等待当前任务自我终止或运行完成后才能被调度执行.
  3. 抢占式调度算法
    1)基于时钟中断的抢占式优先权调度算法.
    实时任务到达后,若是该任务的优先级别高于当前任务的优先级并不当即抢占当前任务的处理机,而是等到时钟中断到来时,调度程序才剥夺当前任务的执行,将处理机分配给新到的高优先权任务.
    2)当即抢占的优先权调度算法.
    在这种调度策略中,要求操做系统具备快速响应外部时间中断的能力.一旦出现外部中断,只要当前任务未处于临界区便当即剥夺当前任务的执行,把处理机分配给请求中断的紧迫任务,实时进程调度,实时进程抢占当前。

<div id="2_16"></div>

2.1六、 Linux下你经常使用的命令有哪些?

这个最好是多用用比较好,直接跳过看一下个题

一、显示日期的指令: date
二、显示日历的指令:cal
三、简单好用的计算器:bc
怎么10/100会变成0呢?这是由于bc预设仅输出整数,若是要输出小数点下位数,那么就必需要执行 scale=number ,那个number就是小数点位数,
四、重要的几个热键[Tab],[ctrl]-c, [ctrl]-d
[Tab]按键---具备『命令补全』不『档案补齐』的功能
[Ctrl]-c按键---让当前的程序『停掉』
[Ctrl]-d按键---一般表明着:『键盘输入结束(End Of File, EOF 戒 End OfInput)』的意思;另外,他也能够用来取代exit
五、man
退出用q,
man -f man
六、数据同步写入磁盘: sync
输入sync,那举在内存中还没有被更新的数据,就会被写入硬盘中;因此,这个挃令在系统关机戒从新启劢乀前, 径重要喔!最好多执行几回!
七、惯用的关机指令:shutdown
此外,须要注意的是,时间参数请务必加入指令中,不然shutdown会自动跳到 run-level 1 (就是单人维护的登入状况),这样就伤脑筋了!底下提供几个时间参数的例子吧:
重启,关机: reboot, halt,poweroff
八、切换执行等级: init
Linux共有七种执行等级:
--run level 0 :关机
--run level 3 :纯文本模式
--run level 5 :含有图形接口模式
--run level 6 :从新启动
使用init这个指令来切换各模式:
若是你想要关机的话,除了上述的shutdown -h now以及poweroff以外,你也能够使用以下的指令来关机:
九、改变文件的所属群组:chgrp
十、改变文件拥有者:chown
他还能够顸便直接修改群组的名称
十一、改变文件的权限:chmod
权限的设定方法有两种, 分别能够使用数字或者是符号来进行权限的变动。
--数字类型改变档案权限:
--符号类型改变档案权限:
十二、查看版本信息等
1三、变换目录:cd
1四、显示当前所在目录:pwd
1五、创建新目录:mkdir
不建议经常使用-p这个选项,由于担忧若是你打错字,那么目录名称就回变得乱七八糟的
1六、删除『空』的目录:rmdir
1七、档案与目录的显示:ls
1八、复制档案或目录:cp
1九、移除档案或目录:rm
20、移动档案与目录,或改名:mv
2一、取得路径的文件名与目录名:basename,dirname
2二、由第一行开始显示档案内容:cat
2三、从最后一行开始显示:tac(能够看出 tac 是 cat 的倒着写)
2四、显示的时候,顺道输出行号:nl
2五、一页一页的显示档案内容:more
2六、与 more 相似,可是比 more 更好的是,他能够往前翻页:less
2七、只看头几行:head
2八、只看尾几行:tail
2九、以二进制的放置读取档案内容:od
30、修改档案时间或新建档案:touch
3一、档案预设权限:umask
3二、配置文件档案隐藏属性:chattr
3三、显示档案隐藏属性:lsattr
3四、观察文件类型:file
3五、寻找【执行挡】:which
3六、寻找特定档案:whereis
3七、寻找特定档案:locate
3八、寻找特定档案:find
3九、压缩文件和读取压缩文件:gzip,zcat
40、压缩文件和读取压缩文件:bzip2,bzcat
4一、压缩文件和读取压缩文件:tar

<div id="2_17"></div>

2.1七、 操做系统什么状况下会死锁?

死锁的4个必要条件
(1) 互斥条件: 一个资源每次只能被一个进程使用。
(2) 请求与保持条件: 一个进程因请求资源而阻塞时,对已得到的资源保持不放。
(3) 不剥夺条件: 进程已得到的资源,在末使用完以前,不能强行被剥夺。
(4) 循环等待条件: 若干进程之间造成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不知足,就不会发生死锁。

竞争不可剥夺资源
在系统中所配置的不可剥夺资源,因为它们的数量不能知足诸进程运行的须要,会使进程在运行过程当中,因争夺这些资源而陷于僵局。
竞争临时性资源
上面所说的打印机资源属于可顺序重复使用型资源,称为永久资源。还有一种所谓的临时资源,这是指由一个进程产生,被另外一个进程使用,短期后便无用的资源,故也称为消耗性资源,

<div id="2_18"></div>

2.1八、 经常使用的hash算法有哪些?

加法Hash;位运算Hash;乘法Hash;除法Hash;查表Hash;混合Hash;

<div id="2_19"></div>

2.1九、 什么是一致性哈希?

一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织
圆环的正上方的点表明0,0点右侧的第一个点表明1,以此类推,
0点的左侧是2的32次方-1,把这个圆环叫作Hash环
而后把服务器ip或者主机名字做为关键字Hash,每一个服务器都能肯定位置,把数据进行相同的Hash算出的位置,顺时针访问的第一个就是对应的服务器

一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,具备较好的容错性和可扩展性。

<div id="2_20"></div>

2.20、 如何理解分布式锁?

分布式锁: 甲乙两人购买时剩余量都显示一个,若是同时下单可能会出现问题,这时就须要用到分布式锁
分布式锁是实现有序调度不一样的进程,解决不一样进程之间相互干扰的问题的技术手段

分布式锁的应具有的条件
在分布式系统环境下,分布式锁在同一个时间仅能被同一个进程访问
高可用的获取/释放锁
高性能的获取/释放锁
具有锁的重入性
具有锁的失效机制,防止死锁
具有非阻塞锁的特性,即便没有获取锁也能直接返回结果

分布式锁的实现有哪些
mechache:
利用mechache的add命令,改命令是原子性的操做,只有在key 不存在的状况下,才能add成功,也就意味着线程拿到了锁
Redis:
和Mechache的实现方法类似,利用redis的setnx命令,此命令一样是原子性的操做,只有在key不存在的状况下,add成功
zookeeper:
利用他的顺序临时节点,来实现分布式锁和等待队列,zookeeper的设计初衷就是为了实现分布式微服务的

使用Redis实现分布式锁的思路
先去redis中使用setnx(商品id,数量) 获得返回结果
这里的数量无所谓,它的做用就是告诉其余服务,我加上了锁
发现redis中有数量,说明已经能够加锁了
发现redis中没有数据,说明已经得到到了锁
解锁: 使用redis的 del商品id
锁超时, 设置exprie 生命周期,如30秒, 到了指定时间,自定解锁
三个致命问题
非原子性操做:setnx,宕机,expire
由于 setnx和expire不是原子性的,要么都成功要么都失败, 一旦出现了上面的状况,就会致使死锁出现
redis提供了原子性的操做 set ( key , value , expire)
误删锁
假如咱们的锁的生命事件是30秒,结果我在30s内没操做完,可是锁被释放了
jvm2拿到了锁进行操做
jvm1 操做完成使用del,结果把jvm2 的锁删除了
解决方法, 在删除以前,判断是否是本身的锁
redis提供了原子性的操做 set ( key ,threadId, expire)
超时为完成任务
增长一个守护线程,当快要超时,可是任务还没执行完成,就增长锁的时间

<div id="2_21"></div>

2.2一、 数据库中的范式有哪些?

第一范式----数据库中的表(全部字段值)都是不可分割的原子数据项。
第二范式----数据库表中的每一列都和主键相关,而不能只和主键的某一部分相关。也就是说 一个表中只能只能包含一个,不能把多种数据保存在同一个表中。
第三范式----数据库表中每一列数据都和主键直接相关,不能间接相关。

<div id="2_22"></div>

2.2二、 数据库中的索引的结构?什么状况下适合建索引?

数据库中索引的结构是一种排序的数据结构。是经过B树和变形的B+树实现的。

适合建索引: 常常查询,使用,用在表链接的字段(常常更改的字段不适合建索引)

<div id="2_23"></div>

2.2三、 Java中的NIO,BIO,AIO分别是什么?

BIO: 同步并阻塞,服务器实现模式为一个链接一个线程,即客户端有链接请求时服务器端就须要启动一个线程进行处理,若是这个链接不作任何事情会形成没必要要的线程开销,固然能够经过线程池机制改善。BIO方式适用于链接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4之前的惟一选择,但程序直观简单易理解。
NIO: 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到链接有I/O请求时才启动一个线程进行处理。NIO方式适用于链接数目多且链接比较短(轻操做)的架构,好比聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理.AIO方式使用于链接数目多且链接比较长(重操做)的架构,好比相册服务器,充分调用OS参与并发操做,编程比较复杂,JDK7开始支持。

<div id="2_24"></div>

2.2四、 用什么工具调试程序?JConsole,用过吗?

JConsole在JDK/bin目录下面,对资源消耗和性能进行监控,提供图表和可视化界面,占内存小(具体的一些仍是本身多打开看一看就差很少了)

<div id="2_25"></div>

2.2五、 如今JVM中有一个线程挂起了,如何用工具查出缘由?

经过Javacore了解线程运行状况:
Javacore,也能够称为“threaddump”或是“javadump”,它是 Java 提供的一种诊断特性,可以提供一份可读的当前运行的 JVM 中线程使用状况的快照。即在某个特定时刻,JVM 中有哪些线程在运行,每一个线程执行到哪个类,哪个方法。
应用程序若是出现不可恢复的错误或是内存泄露,就会自动触发 Javacore 的生成。而为了性能问题诊断的须要,咱们也会主动触发生成 Javacore。
在 AIX、Linux、Solaris 环境中,咱们一般使用 kill -3 <PID> 产生该进程的 Javacore。

<div id="2_26"></div>

2.2六、 线程同步与阻塞的关系?同步必定阻塞吗?阻塞必定同步吗?

同步是个过程,阻塞是线程的一种状态。多个线程操做共享变量时可能会出现竞争。这时须要同步来防止两个以上的线程同时进入临界区,在这个过程当中,后进入临界区的线程将阻塞,等待先进入的线程走出临界区。
线程同步不必定发生阻塞!!!线程同步的时候,须要协调推动速度,互相等待和互相唤醒会发生阻塞。
一样,阻塞也不必定同步。

<div id="2_27"></div>

2.2七、 同步和异步有什么区别?

同步交互: 指发送一个请 求,须要等待返回,而后 才可以发送下一个请求,有个等待过程;

异步交互: 指发送一个请求,不须要等待返回,随时能够再发送下一个请求,即不须要等待。
区别: 一个须要等待,一个不须要等待,在部分状况下,咱们的项目开发中都会优先选择不须要等待的异步交互方式。

<div id="2_28"></div>

2.2八、 线程池用过吗?

线程池作的工做
主要是控制运行的线程的数量,处理过程当中将任务放入队列,而后在线程建立后启动这些任务,若是线程数量超过了最大数量超出数量的线程排队等候,等其余线程执行完毕,再从队列中取出任务来执行。

线程池的优点
线程复用、控制最大并发数、线程管理
(1)下降系统资源消耗,经过重用已存在的线程,下降线程建立和销毁形成的消耗;
(2)提升系统响应速度,当有任务到达时,经过复用已存在的线程,无需等待新线程的建立便能当即执行;
(3)方便线程并发数的管控。由于线程如果无限制的建立,可能会致使内存占用过多而产生OOM,而且会形成cpu过分切换(cpu切换线程是有时间成本的(须要保持当前执行线程的现场,并恢复要执行线程的现场))。
(4)提供更强大的功能,延时定时线程池。

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

一、corePoolSize(线程池基本大小): 当向线程池提交一个任务时,若线程池已建立的线程数小于corePoolSize,即使此时存在空闲线程,也会经过建立一个新线程来执行该任务,直到已建立的线程数大于或等于corePoolSize时,(除了利用提交新任务来建立和启动线程(按需构造),也能够经过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提早启动线程池中的基本线程。)
二、maximumPoolSize(线程池最大大小): 线程池所容许的最大线程个数。当队列满了,且已建立的线程数小于maximumPoolSize,则线程池会建立新的线程来执行任务。另外,对于*队列,可忽略该参数。
三、keepAliveTime(线程存活保持时间): 当线程池中线程数大于核心线程数时,线程的空闲时间若是超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
四、workQueue(任务队列): 用于传输和保存等待执行任务的阻塞队列。
五、threadFactory(线程工厂): 用于建立新线程。threadFactory建立的线程也是采用new Thread()方式,threadFactory建立的线程名都具备统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
六、handler(线程饱和策略):** 当线程池和队列都满了,再加入线程会执行此策略。

线程池为何须要使用(阻塞)队列?
一、由于线程如果无限制的建立,可能会致使内存占用过多而产生OOM,而且会形成cpu过分切换。
二、建立线程池的消耗较高。 (线程池建立线程须要获取mainlock这个全局锁,影响并发效率,阻塞队列能够很好的缓冲。)

线程池为何要使用阻塞队列而不使用非阻塞队列?
阻塞队列能够保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源。
当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。
使得在线程不至于一直占用cpu资源。
不用阻塞队列也是能够的,不过实现起来比较麻烦而已,有好用的为啥不用呢?

如何配置线程池

CPU密集型任务: 尽可能使用较小的线程池,通常为CPU核心数+1。
IO密集型任务: 能够使用稍大的线程池,通常为2*CPU核心数。
混合型任务: 能够将任务分红IO密集型和CPU密集型任务,

<div id="2_29"></div>

2.2九、 如何建立单例模式?说了双重检查,他说不是线程安全的。如何高效的建立一个线程安全的单例?

饿汉模式
经过定义final型的对象,来让加载类的时候,直接建立对象,只加载一次,实现单例。
懒汉式
经过定义静态对象,加锁去实例化对象。
枚举
经过定义枚举类,来实现单例。

Double-Check
如有两个线程经过了第一个Check循环,进入第二个Check循环是串行化的,只能有一个线程进入,这样当这个线程建立完成后,另外的线程就没法经过第二个循环了,保证了实例的惟一性,随后的线程也不会经过第一个Check循环,也就不会有同步控制的环节了。可是,这种方法也伴随着一个缺点,它可能会引发空指针的异常。
高效建立线程安全的单例

Volatile+Double-Check
 volatile关键字能够防止重排序的发生,在此不对volatile做详细介绍,经过volatile关键字,这种模式能够说是知足懒加载、多线程下单例的惟一性、安全性的。

public final class SingletonObject5 {

    private volatile static SingletonObject5 instance ;

    private SingletonObject5() {
    }

    public static SingletonObject5 getInstance() {
        if (null == instance) {
            synchronized (SingletonObject5.class) {
                if (null == instance)
                    instance = new SingletonObject5();
            }
        }
        return SingletonObject5.instance;
    }
}

<div id="2_30"></div>

2.30、 concurrent包下面,都用过什么?

(凉凉夜色为我思念成河……)

1.executor接口,使用executor接口的子接口ExecutorService用来建立线程池
2.Lock接口下的ReentrantLock类,实现同步,好比三个线程循环打印ABCABCABC...
3.atomic包,使用AtomicInteger类的incrementAndGet()方法来实现原子操做,好比a++
4.Callable接口,重写call方法,实现多线程
5.concarrenHashMap,线程安全的HashMap

<div id="2_31"></div>

2.3一、 经常使用的数据库有哪些?redis用过吗?

mysql 、SQL Server、Oracle、Sybase、DB2等
Redis:
一、纯内存操做
二、核心是基于非阻塞的IO多路复用机制
三、单线程反而避免了多线程的频繁上下文切换问题

<div id="2_32"></div>

2.3二、 了解hadoop吗?说说hadoop的组件有哪些?说下mapreduce编程模型。

   common、Hadoop Distributed File System(HDFS)、MapReduce、YARN

编程模型:

Mapper把复杂的任务分解为若干个小任务,分到存在所需数据结点上进行计算,这些任务能够一块儿,彼此没有依赖关系
Reducer把Mapper的结果汇总
eg:
一共有100个汉堡,甲吃十个,乙吃是个,丙吃十个,,,这就是Mapper
甲乙丙……放到一块儿就是Reducer

<div id="2_33"></div>

2.3三、 你知道的开源协议有哪些?

• Mozilla Public License:
MPL License,容许免费重发布、免费修改,但要求修改后的代码版权归软件的发起
者。这种受权维护了商业软件的利益,它要求基于这种软件得修改无偿贡献版权给该软件。这样,围绕该软件得
全部代码得版权都集中在发起开发人得手中。但MPL是容许修改,免费使用得。MPL软件对连接没有要求。
• BSD开源协议:
给于使用者很大自由的协议。能够自由的使用,修改源代码,也能够将修改后的代码做为开源或
者专有软件再发布。
• Apache Licence 2.0 :
Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD相似,一样
鼓励代码共享和尊重原做者的著做权,一样容许代码修改,再发布(做为开源或商业软件)。
• GPL:
GPL许可证是自由软件的应用最普遍的软件许可证,人们能够修改程式的一个或几个副本或程式的任何部
分,以此造成基於这些程式的衍生做品。必须在修改过的档案中附有明显的说明:您修改了此一档案及任何修改
的日期。 您必须让您发布或出版的做品,包括本程式的所有或一部分,或内含本程式的所有或部分所衍生的做
品,容许第三方在此许可证条款下使用,而且不得由于此项受权行为而收费。
• LGPL:
LGPL是GPL的一个为主要为类库使用设计的开源协议。和GPL要求任何使用/修改/衍生之GPL类库的的软
件必须采用GPL协议不一样。LGPL容许商业软件经过类库引用(link)方式使用LGPL类库而不须要开源商业软件的代
码。这使得采用LGPL协议的开源代码能够被商业软件做为类库引用并发布和销售。

• Public Domain:
公共域受权。将软件受权为公共域,这些软件包没有受权协议,任何人均可以随意使用它

<div id="2_34"></div>

2.3四、 你知道的开源软件有哪些?

• JDK
• eclipse
• Tomcat
• Spring
• Hibernate
• MySQL
• MyBatis
• struts

<div id="2_35"></div>

2.3五、 你最近在看的书有哪些?

这个尽可能说真话,最好不要搜一点简介就说看过什么书,不然被戳穿很难受,
若是实在没看过能够这么说:
最近没怎么看书,可是相对于书本,我更喜欢在B站学习,好比大学公开课和摄影栏目我就常常逛;知乎我也挺活跃的,关注……等话题,天天保持至关的阅读量,回答受赞也有几百了;我也参加了不少经验分享活动,我以为从面对面的交流得到的知识,是阅读没法替代的。

<div id="2_36"></div>

2.3六、 你有什么问题要问我吗?

严禁:没问题,提薪资,五年计划,某某产品被卖了等可有可无的问题
准备3-5个问题就行,太多或者太少都很差
问题
一、问工做内容
这份工做比较大的挑战是?
您但愿我在短时间内解决哪些问题?
对于将来加入这个团队,您对个人指望是什么?
我对这个职位工做的理解是XXX,不知道除了个人理解外,是否还有其余的工做职责?
二、问职位差距
对我这次应聘的表现有什么评价和建议?
若是能够录用,我须要学习哪方面的知识?
接下来的这段空档期,有什么值得注意或者建议学习的吗?
三、问工做潜力
请问该岗位都要经历哪些培训?
这个岗位的晋升路径是什么样子的?
我们部门近期/将来有什么新动向/新举措?
您对这个岗位三到五年职业规划的建议是什么呢
四、问团队氛围
能带我看一下办公区吗?
您在公司的一天是如何度过的?
能够介绍下一块儿工做的团队是什么样的吗?

提问的原则
一、探讨式发问
提问不是只问不答,提问后,先抛出一点本身的理解,让面试官看出你对应聘的职位是作过功课的。透过发问,更了解公司的组织文化和工做上实际会遇到的问题。
二、在其位谋其职
提问要展示专业度,切忌太跳脱或者故意装高深,引起尴尬,又给人好高骛远的感受。记住一点:不提和所聘岗位无关的问题。
三、真诚
提问环节也是考验情商的时候!关注他人感觉,掌握好分寸感。要明白提问不是辩论,你不是为了和面试官互相切磋知识、技能,而是真诚、虚心请教。

<div id="2_37"></div>

2.3七、 了解哪些设计模式?说说都用过哪些设计模式

设计模式的分类

整体来讲是三大类
建立型模式 ,五种:工厂方法模式、 抽象工厂模式、单例模式、建造者模式、原型模式
结构性模式 ,共七种: 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介模式、解释器模式。

其实还有两类:并发型模式和线程池模式

<div id="2_38"></div>

2.3八、 如何判断一个单链表是否有环?

快慢指针查询,快指针每次跳两个,慢指针每次跳一个,若是两指针相等时,就证实有环
环的入口:
用两个指针,一个指向快慢指针相交点(这里就是慢指针走,慢指针在走快指针的一半就至关于快指针走的路了,还会到这个点),一个指向单链表的头结点(模拟慢指针从头走,也是走快指针的一半),一块儿走,当两个指针相等时,就是环的入口。

数学思惟:
设从单链表头节点到环入口节点的距离是D,环入口到相交点的距离是X,设slow和fast第一次相遇时fast走了n圈环,slow走的距离为len,那么fast走的距离是2*len,能够得出下面的两个等式:

len = D + X
2 * len = D + X + n * R

两个等式相减能够的到:len = n * R - X

若是还不行的话,本身画个带环的单链表就明白了

<div id="2_39"></div>

2.3九、 操做系统如何进行分页调度?

笔者不懂,凉凉……

 1.分页的做用: 高效率地利用内存,能够运行比物理内存空间更大的程序;
2.分页机制的原理: 利用两级页表将内存分割成4KB/页的大小,强制内存对齐提升效率;
3.页表结构: PDE与PTE在内存中各个位的主要做用,表项与页之间的对应关系。

<div id="2_40"></div>

2.40、 匿名内部类是什么?如何访问在其外面定义的变量?

匿名内部类:
没有名字,一般用来简化代码,只能用一次(使用的话必需要继承父类或者实现接口)

访问在其外面定义的变量:
必需要final类型的变量(也能够不用final类型,但只能使用不能修改)

三面

<div id="3_1"></div>

3.一、 自我介绍,作过什么项目。

这。就不说了,某度一搜一堆,项目要挑本身作的最有水平的拿出来

<div id="3_2"></div>

3.二、java虚拟机的区域如何划分

程序计数器(独立内存)
当前线程所执行的字节码的行号指示器。
Java虚拟机栈(独立内存)
每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
本地方法栈(独立内存)
本地方法栈则为虚拟机使用到的Native方法(百度说是Java中声明的可调用的C/C++实现的方法)服务。
Java堆(共享内存): 存放对象实例
方法区(共享内存): 存储已被虚拟区加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池: 存放编译期生成的各类字面量和符号引用,这部份内容将在类加载后进入方法区的运行时常量池中存放。

<div id="3_3"></div>

3.三、 双亲委派模型中,从顶层到底层,都是哪些类加载器,分别加载哪些类?

(1)启动类加载器(Bootstrap ClassLoader)
这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,而且是虚拟机识别的类库加载到虚拟机内存中。启动类加载器没法被Java程序直接引用。
(2)扩展类加载器(Extension ClassLoader)
这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的全部类库,开发者能够直接使用扩展类加载器
(3)应用程序类加载器(Application ClassLoader)
这个加载器是ClassLoader中getSystemClassLoader()方法的返回值,因此通常也称它为系统类加载器。它负责加载用户类路径(Classpath)上所指定的类库,可直接使用这个加载器,若是应用程序没有自定义本身的类加载器,通常状况下这个就是程序中默认的类加载器

<div id="3_4"></div>

3.四、 有没有可能父类加载器和子类加载器,加载同一个类?若是加载同一个类,该使用哪个类?

双亲委派,先在父类看能不能加载,若是能则由父加载,不然给子类加载

<div id="3_5"></div>

3.五、 HashMap的结构,get(),put()是如何实现的?

Put:
一、先将key和value封装到Node节点中
二、底层会调用key的hashcode()方法,经过hash函数将hash值转换为数组下标,下标位置上若是没有任何元素,就把该Node添加到该位置上(该下标处)
若是该下标处对应的位置上已经存在元素或链表(多于一个元素变成链表),那么就会拿着新节点的key与链表上的每个人节点中的key进行equals。
一、 若是全部对比(equals)都返回false,那么这个新节点将会被添加到链表的尾部。(大于8个就会转换成红黑树)
二、 若是其中有一个对比(equals)返回true,那么这个节点上的value将会被新节点的value覆盖。

Get:
一、底层会调用key的hashcode()方法,经过hash函数将hash值转换为数组下标,经过数组下标快速定位到数组的指定位置上,若是这个位置上没有任何元素,那么返回null。
二、若是这个位置上有单向链表(该位置上有元素,或者有红黑树),那么会拿着咱们get(key)中的key和单向链表中的每一个节点的key进行equals,若是说全部的equals都返回false,那么这个get方法返回false。
三、只要其中有一个节点的key和参数key的equals对比的结果返回true,那么这个节点的value就是咱们想要找的value,get方法返回这个value.

<div id="3_6"></div>

3.六、 ConcurrentHashMap的get(),put(),又是如何实现的?ConcurrentHashMap有哪些问题? ConcurrentHashMap的锁是读锁仍是写锁?

put 操做一上来就锁定了整个segment,这固然是为了并发的安全,修改数据是不能并发进行的,必须得有个判断是否超限的语句以确保容量不足时可以 rehash,
而比较难懂的是这句int index = hash & (tab.length - 1),原来segment里面才是真正的hashtable,即每一个segment是一个传统意义上的hashtable, 从二者的结构就能够看出区别,这里就是找出须要的entry在table的哪个位置,以后获得的entry就是这个链的第一个节点,若是e!=null,说明找到了,这是就要替换节点的值(onlyIfAbsent == false),不然,咱们须要new一个entry,它的后继是first,而让tab[index]指向它,什么意思呢?实际上就是将这个新entry 插入到链头,剩下的就很是容易理解了。

get 方法(请注意,这里分析的方法都是针对桶的,由于ConcurrentHashMap的最大改进就是将粒度细化到了桶上),首先判断了当前桶的数据个数是 否为0,为0天然不可能get到什么,只有返回null,这样作避免了没必要要的搜索,也用最小的代价避免出错。而后获得头节点(方法将在下面涉及)以后就 是根据hash和key逐个判断是不是指定的值,若是是而且值非空就说明找到了,直接返回;
程序很是简单,但有一个使人困惑的地方,这句return readValueUnderLock(e)究竟是用来干什么的呢?研究它的代码,在锁定以后返回一个值。但这里已经有一句V v = e.value获得了节点的值,这句return readValueUnderLock(e)是否画蛇添足?
事实上,这里彻底是为了并发考虑的,这里当v为空时,多是一个线程正在改变节点,而以前的 get操做都未进行锁定,根据bernstein条件,读后写或写后读都会引发数据的不一致,因此这里要对这个e从新上锁再读一遍,以保证获得的是正确值,
这里不得不佩服Doug Lea思惟的严密性。整个get操做只有不多的状况会锁定,相对于以前的Hashtable,并发是不可避免的啊!

ConcurrentHashmap只能保证自身数据在多线程的环境下不被破坏,而并不能保证业务逻辑的正确性。

ConcurrentHashMap的锁是读锁

<div id="3_7"></div>

3.七、 sleep()和wait()分别是哪一个类的方法,有什么区别?synchronized底层如何实现的?用在代码块和方法上有什么区别?

sleep方法是Thread类的静态方法,调用此方法会让当前线程暂停指定的时间,将执行机会(CPU)让给其余线程,可是不会释放锁,所以休眠时间结束后自动恢复(程序回到就绪状态)。
wait是Object类的方法,调用对象的wait方法致使线程放弃CPU的执行权,同时也放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify或notifyAll方法才能唤醒等待池中的线程进入等锁池(lock pool),若是线程从新得到对象的锁就能够进入就绪状态。

wait只能在同步控制方法中或者同步控制块中使用,而sleep能够在任何地方使用。
wait 能够指定时间也能够不指定,指定时间 wait(time) 在 time时间内 有别的线程 notifyAll() 是不会唤醒到它 。sleep 必须指定时间

synchronized代码块是由一对monitorenter/monitorexit指令实现的, Monitor对象是同步的基本实现单元。
现代的(Oracle) JDK6中, JVM对此进行了大刀阔斧地改进,提供了三种不一样的Monitor实现,也就是常说的三种不一样的锁
偏斜锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。

同步方法直接在方法上加synchronized实现加锁,同步代码块则在方法内部加锁,很明显,同步方法锁的范围比较大,而同步代码块范围要小点
通常同步的范围越大,性能就越差,通常须要加锁进行同步的时候,确定是范围越小越好,这样性能更好。

<div id="3_8"></div>

3.八、 什么是线程池?若是让你设计一个动态大小的线程池,如何设计,应该有哪些方法?

线程池就是建立若干个可执行的线程放入一个池(容器)中,有任务须要处理时,会提交到线程池中的任务队列,处理完以后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

一个线程池包括如下四个基本组成部分:
线程管理器 (ThreadPool): 用于建立并管理线程池,包括建立线程,销毁线程池,添加新任务;
工做线程 (PoolWorker): 线程池中线程,在没有任务时处于等待状态,能够循环的执行任务;
任务接口 (Task): 每一个任务必须实现的接口,以供工做线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工做,任务的执行状态等;
任务队列 (TaskQueue): 用于存放没有处理的任务。提供一种缓冲机制;

所包含的方法


//建立线程池
private ThreadPool() 
//得到一个默认线程个数的线程池
public static ThreadPool getThreadPool() 
//执行任务,其实只是把任务加入任务队列,何时执行有线程池管理器决定
public void execute(Runnable task) 
//批量执行任务,其实只是把任务加入任务队列,何时执行有线程池管理器决定
public void execute(Runnable[] task)
//销毁线程池,该方法保证在全部任务都完成的状况下才销毁全部线程,不然等待任务完成才销毁
public void destroy()
//返回工做线程的个数
public int getWorkThreadNumber()
//返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能该任务并无实际执行完成
public int getFinishedTasknumber()
//在保证线程池中全部线程正在执行,而且要执行线程的个数大于某一值时。增长线程池中线程的个数
public void addThread()
//在保证线程池中有很大一部分线程处于空闲状态,而且空闲状态的线程在小于某一值时,减小线程池中线程的个数
public void reduceThread()

<div id="3_9"></div>

3.九、 什么是死锁?JVM线程死锁,你该如何判断是由于什么?若是用VisualVM,dump线程信息出来,会有哪些信息?

(不懂仍是不懂,