point

1 HashMap

  底层是数组和链表,通过key的哈希码找数组,找到了才通过key判断,相等的话直接更新值,不等的话链表后面加上

  put,get方法要记

 

   hashmap的扩容需要满足两个条件:。当前数据存储的数量(size)大小必须大于等于阈值;当前加入的数据是否发生了散列冲突(扩容* 2)

 

因为上面这两个条件,所以存在下面这些情况

(1),就是散列映射在存值的时候(默认大小为16,负载因子0.75,阈值12),可能达到最后存满16个值的时候,再存入第17个值才会发生扩容现象,因为前16个值,每个值在底层数组中分别占据一个位置,并没有发生散列碰撞。

(2),当然也有可能存储更多值(超过16个值,最多可以存26个值)都还没有扩容原理:前11个值全部散列碰撞,存到数组的同一个位置(这时元素个数小于阈值12,不会扩容),后面所有存入的15个值全部分散到数组剩下的15个位置(这时元素个数大于等于阈值,但是每次存入的元素并没有发生散列碰撞,所以不会扩容),前面11 + 15 = 26,所以在存入第27个值的时候才同时满足上面两个条件,这时候才会发生扩容现象。

 

2concurrentHashMap

  多了一个桶的概念,每次对桶进行加锁,其他的桶其他线程照样访问(jdk1.7)

jdk1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在jdk1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本。

3hashtable

 对hashmap每个方法都加锁的话那就是hashtable,效率低下

 

4线程池

  一个新的任务提交到线程池之后,线程池是如何处理的
 1,线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步
 2,线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果工作队列满了,则执行第三步
3中,线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务
线程池饱和策略
这里提到了线程池的饱和策略,那我们就简单介绍下有哪些饱和策略:
AbortPolicy
为Java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要尝试捕捉,否则程序会直接退出
DiscardPolicy
接抛弃,任务不执行,空方法
DiscardOldestPolicy
从队列里面抛弃头部的一个任务,并再次执行此任务
CallerRunsPolicy
在调用执行的线程里面执行此命令,阻塞会入口
用户自定义拒绝策略(最常用)
实现RejectedExecutionHandler,并自己定义策略模式

 

4.5 线程的几种状态

1 新建状态

2 就绪状态,线程创建之后,其他对象调用了该对象的start方法,则该线程位于可运行线程池中,变得可运行

3 运行状态 

4 阻塞状态 

5 死亡状态 执行完了或者因为异常退出了run方法

 

5线程同步(同步的,易失性,ReenreantLock,ThreadLocal) 

如果是只读的  没有问题

如果还可以进行写操作的,则要加锁

https://blog.csdn.net/yoonerloop/article/details/81154596

 

6设计模式 

  工厂模式 

  责任链模式:拦截的类都实现统一接口。

  单例模式:配置文件的解析

  代理模式:AOP(一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。)AOP应用:日志的记录,权限的控制

 

7数据库隔离级别

1.READ UNCIMMITTED(未提交读)这就是事务还没提交,而别的事务可以看到他其中修改的数据的后果,也就是脏读。

2 READ COMMITTED(大多数数据库默认的)这就是小华的事务执行到一半,而小明看不到他执行的操作,所以看到的是旧数据,也就是无效的数据

3 REPEATABLE READ(可重复)虽然读取同一条数据可以保证一致性,但是却不能保证没有插入新的数据

4.SERIALIZABLE(可串行化)SERIALIZABLE是最高的隔离级别,它通过强制事务串行执行(注意是串行),避免了前面的幻读情况,由于他大量加上锁,导致大量的请求超时,因此性能会比较底下,再特别需要数据一致性且并发量不需要那么大的时候才可能考虑这个隔离级别

 

8二叉树红黑树B-树B +树

B+树的特点是只有底层的子节点才会存数据,互相有指针指向,直接遍历底层就好

B树需要中序遍历

 

 

9基本数据类型

Java基本数据类型byte short int long float char double boolean

 

10 arrayList,linkedList的区别

 ArrayList的中的中的是基于数据实现的名单,而链表的是基于链表实现的列表。所以,ArrayList中中中的中拥有着数组的特性,链表的拥有着链表的特性。

  • 优缺点

  数组列表

  优点:适合随机读取的时候,读取速度快,可以一步GET(指数)。

  缺点:添加值很慢 - 一方面,添加数据在阵列中间的时候,需要移动后面的数;另一方面,当长度大于初始长度的时候,每添加一个数,都会需要扩容。

  链表的:双向链表

  优点:添加值很快 - 添加在列表中间也只需要更改指针,长度不固定。

  实现栈和队列方面,链表的要优于ArrayList的中的中的。

 

11为什么用b +树做索引

https://blog.csdn.net/weixin_30531261/article/details/79312676


B+树还有一个最大的好处,方便扫库,B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持range-query非常方便,而B树不支持。这是数据库选用B+树的最主要原因。 
比如要查 5-10之间的,B+树一把到5这个标记,再一把到10,然后串起来就行了,B树就非常麻烦。B树的好处,就是成功查询特别有利,因为树的高度总体要比B+树矮。不成功的情况下,B树也比B+树稍稍占一点点便宜。 
B树比如你的例子中查,17的话,一把就得到结果了, 
有很多基于频率的搜索是选用B树,越频繁query的结点越往根上走,前提是需要对query做统计,而且要对key做一些变化。 
另外B树也好B+树也好,根或者上面几层因为被反复query,所以这几块基本都在内存中,不会出现读磁盘IO,一般已启动的时候,就会主动换入内存。”
 

12 Session和cookie的区别

cookie通过在客户端的的记录信息确定用户身份,session通过在服务器端的的的记录信息确定用户身份

 

13Redis为啥存取速度这么快?内部是单线程还是多线程实现?单线程为啥这么快? 

官方提供的数据是可以达到100000+的QPS(每秒内查询次数)单线程 - 多路复用IO模型

如图1所示,完全基于内存,绝大部分请求是纯粹的内存操作,非常快速数据存在内存中,类似于HashMap中中中,哈希映射中的优势就是查找和操作的时间复杂度都是ö (1)。
如图2所示,数据结构简单,对数据操作也简单,Redis的中的数据结构是专门进行设计的;
3,采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4,使用多路I / O复用模型,非阻塞IO;
5,使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis的的的直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

为什么是单线程

我们首先要明白,上边的种种分析,都是为了营造一个Redis很快的氛围!官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!) 。

看到这里,你可能会气哭!本以为会有什么重大的技术要点才使得Redis的的的的使用单线程就可以这么快,没想到就是一句官方看似糊弄我们的回答!但是,我们已经可以很清楚的解释了为什么Redis的的的的这么快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了!

但是,我们使用单线程的方式是无法发挥多核CPU性能,不过我们可以通过在单机开多个Redis的的的实例来完善!

缺点:不能发挥多核的作用

 

14乐观锁悲观锁

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java的的的的里面的同步原语同步关键字的实现也是悲观锁。

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java的的的的中java.util.concurrent中。原子中中的中的包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

 

15数据库优化方案

    缓存,消息队列,分库分表,读写分离

 

16 jvm内存模型

分为堆,栈,静态方法区

堆内存:被所有线程共享的区域,JVM启动时存放对象的地方

   

一块的的Java的堆空间一般分成三部分,这三部分用来存储三类数据:

  • 刚刚创建的对象。在代码运行时会持续不断地创造新的对象,这些新创建的对象会被统一放在一起。因为有很多局部变量等在新创建后很快会变成不可达的对象,快速死去,因此这块区域的特点是存活对象少,垃圾多形象点描述这块区域为:新生代eden和幸存者survival比例1:8
  • 工作原理如下:

  • 1首先,Eden区最大,对外提供堆内存。当Eden区快要满了,则进行Minor GC,把存活对象放入Survivor A区,清空Eden区;
  • 2Eden区被清空后,继续对外提供堆内存;
  • 3当Eden区再次被填满,此时对Eden区和幸存者A区同时进行Minor GC,把存活对象放入幸存者B区,同时清空Eden区和幸存者A区;
  • 4Eden区继续对外提供堆内存,并重复上述过程,即在伊甸区填满后,把伊甸区和某个幸存者区的存活对象放到另一个幸存者区;
  • 5当某个幸存者区被填满,且仍有对象未被复制完毕时,或者某些对象在反复存活15次左右时,则把这部分剩余对象放到旧区;
  • 6当旧区也被填满时,进行Major GC,对旧区进行垃圾回收。
  •  
  • 存活了一段时间的对象。这些对象早早就被创建了,而且一直活了下来。我们把这些存活时间较长的对象放在一起,它们的特点是存活对象多,垃圾少。形象点描述这块区域为:老年代;
  • 永久存在的对象。比如一些静态文件,这些对象的特点是不需要垃圾回收,永远存活。形象点描述这块区域为:永久代。(不过在Java 8里已经把永久代删除了,把这块内存空间给了元空间。)

栈:可创建线程的时候指定大小,线程数=空闲/线程栈的大小

 

 

17垃圾回收机制

判断是否存活的两种方法

1引用计数发:为每一个创建的对象分配一个引用计数器,用来存储该对象被引用的个数当该个数为零,意味着没有人再使用这个对象,可以认为“对象死亡”但是,这种方案存在严重的问题,就是无法检测“循环引用”:当两个对象互相引用,即时它俩都不被外界任何东西引用,它俩的计数都不为零,因此永远不会被回收。而实际上对于开发者而言,这两个对象已经完全没有用处了。

2可达性分析:这种方案是目前主流语言里采用的对象存活性判断方案。基本思路是把所有引用的对象想象成一棵树,从树的根结点GC Roots出发,持续遍历找出所有连接的树枝对象,这些对象则被称为“可达”对象,或称“存活”对象。其余的对象则被视为“死亡”的“不可达”对象,或称“垃圾”。

处理垃圾的方法

1标记 - 清除:谓“标记”就是利用可达性遍历堆内存,把“存活”对象和“垃圾”对象进行标记,得到的结果如上图;
第二步,既然“垃圾”已经标记好了,那我们再遍历一遍,把所有“垃圾”对象所占的空间直接清空即可。(容易产生碎片)

2标记 - 整理

3复制

这种方法比较粗暴,直接把堆内存分成两部分,一段时间内只允许在其中一块内存上进行分配,当这块内存被分配完后,则执行垃圾回收,把所有存活对象全部复制到另一块内存上,当前内存则直接全部清空。

 

18字符串作为hashmap的密钥(key)有什么好处其他对象做密钥(key)有什么坏处

总结:在使用字符串类型的对象做键时我们可以只根据传入的字符串内容就能获得对应存在映射中的值,而非字符串类型的对象在获得对应的值时需要的条件太过苛刻,首先要保证散列码相同,并且经过的equals()方法方法判断为真时才可以获得对应的价值。

PS:对象同理(对象若不重写的hashCode()方法的话,的hashCode()方法如何计算出来的本地方法?)
如果你想把自定义的对象作为重点,那也是可以的,你只需要重写的hashCode()方法与equals()方法方法即可,按照你自己的意愿定义散列码。这篇博文属于博主自己的对这个问题理解,如果哪里有不准确或者是错误的理解还请大家指正。

19对象默认的散列码方法

的的的hashCode是所有的的的Java对象的固有方法,如果不重载的话,返回的实际上是该对象在JVM的堆上的内存地址,而不同对象的内存地址肯定不同,所以这个的的的hashCode也就肯定不同了。如果重载了的话,由于采用的算法的问题,有可能导致两个不同对象的的的的hashCode相同。

 

20启动线程的几种方式

的的的Java中有几种方式启动一个线程

1个继承Thread类创建线程
2实现Runnable接口创建线程
3实现可调用接口通过FutureTask包装器来创建线程线程
4使用的ExecutorService的,可赎回,未来实现有返回结果的线程

 

21线程池的好处以及Java的的的有几种线程池

好处:  

    1线程的创建和销毁的开销是巨大的,而通过线程池的重用大大减少了这些不必要的开销,当然既然少了这么多消费内存的开销,其线程执行速度也是突飞猛进的提升。

    2线程池可以对线程进行管理

坏处

   1死锁

   2并发错误

   3线程泄露

常见的几种线程池

 1  newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

 2 newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

 3 newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

 4 newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。

  

22 tcp三次握手,四次挥手为什么三次四次

https://www.cnblogs.com/zhuzhenwei918/p/7465467.html

1客户端向服务端建立连接,发起同包,同步序列编号

2服务端接收到客户端发过来的包,进行确认,自己也发送一个syn包。即syn + ack

  服务器进入接受状态

3客户端收到服务器发送的包,再向服务器发送确认包ACK,建立连接,完成三次握手

https://baike.baidu.com/item/%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B/5111559

 

 我们假设甲和乙是通信的双方。我理解的握手实际上就是通信,发一次信息就是进行一次握手

  • 第一次握手:A给B打电话说,你可以听到我说话吗?
  • 第二次握手:B收到了A的信息,然后对A说:我可以听得到你说话啊,你能听得到我说话吗?  
  • 第三次握手:A收到了B的信息,然后说可以的,我要给你发信息啦!

  在三次握手之后,A和B都能确定这么一件事:我说的话,你能听到;你说的话,我也能听到。  这样,就可以开始正常通信了。

四次挥手:
A:“喂,我不说了(FIN)。”A-> FIN_WAIT1

B:“我知道了(ACK)。等下,上一句还没说完.Balabala ... ..(传输数据)”B-> CLOSE_WAIT | A-> FIN_WAIT2

B:“好了,说完了,我也不说了(FIN)。”B-> LAST_ACK

答:“我知道了(ACK)。”A-> TIME_WAIT | B-> CLOSED

A 等待2MSL,保证B收到了消息,否则重说一次“我知道了”,A->停止营业

 

这样,通过四次挥手,可以把该说的话都说完,并且A和B都知道自己没话说了,对方也没花说了,然后就挂掉电话(断开链接)了。 

SYN洪水攻击的DDos:

SYN-Flood攻击是当前网络上最为常见的DDoS攻击,也是最为经典的拒绝服务攻击,它就是利用了TCP协议实现上的一个缺陷,通过向网络服务所在端口发送大量的伪造源地址的攻击报文,就可能造成目标服务器中的半开连接队列被占满,从而阻止其他合法用户进行访问。这种攻击早在1996年就被发现,但至今仍然显示出强大的生命力。很多操作系统,甚至防火墙,路由器都无法有效地防御这种攻击,而且由于它可以方便地伪造源地址,追查起来非常困难。它的数据包特征通常是,源发送了大量的SYN包,并且缺少三次握手的最后一步握手ACK回复。

SYN请求,服务器回应(SYN + ACK)包,而真实的IP会认为,我没有发送请求,不作回应。服务器没有收到回应,这样的话,服务器不知道(SYN + ACK)是否发送成功,默认情况下会重试5次(tcp_syn_retries)。这样的话,对于服务器的内存,带宽都有很大的消耗。攻击者如果处于公网,可以伪造IP的话,对于服务器就很难根据IP来判断攻击者,给防护带来很大的困难。

23 tcp如何保证可靠传输

1应用数据被分割成TCP认为最适合发送的数据块。

2超时重传:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段如果不能及时收到一个确认,将重发这个报文段。

3)TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层
0.4)校验和:TCP将保持它首部和数据的检验和这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文文和不确认收到此报文段
.5)TCP的接收端会丢弃。重复的数据
6)流量控制:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的我数据当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失.TCP使用的流量控制协议是可变大小的滑动窗口协议
0.7)拥塞控制:当网络拥塞时,减少数据的发送。

 

tcp重传的认识:::

TCP是一种可靠的协议,在网络交互的过程中,由于TCP报文是封装在IP协议中的,IP协议的无连接特性导致其可能在交互的过程中丢失,在这种情况下,TCP协议如何保障其传输的可靠性呢?

TCP通过在发送数据报文时设置一个超时定时器来解决这种问题,如果在定时器溢出时还没有收到来自对端对发送报文的确认,它就重传该数据报文。

24 servlet是线程安全的吗?

Servlet的的不是线程安全的。

要解释为什么的Servlet的为什么不是线程安全的,需要了解的Servlet的容器​​(即Tomcat)是如何响应HTTP请求的。

当的Tomcat的接收到的客户端的HTTP请求时,Tomcat的的从线程池中取出一个线程,之后找到该请求对应的的Servlet的对象并进行初始化,之后调用service()方法。要注意的是每一个的的Servlet对象再Tomcat的的容器中只有一个实例对象,即是单例模式。如果多个HTTP请求请求的是同一个的Servlet中,那么着两个HTTP请求对应的线程将并发调用的Servlet的的服务()方法。

 

25 BIO NIO(都是同步的)AIO(异步非阻塞式IO)

BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I / O请求时才启动一个线程进行处

 

26 Netty处理粘包拆包问题 

产生原因:

(1)应用程序写写入的字节大小大于套接口发送缓冲区大小;
(2)进行MSS大小的TCP分段;
(3)以太网帧的有效载荷大于MTU进行IP分片。

解决办法:

(1)消息定长,例如每个报文的大小为固定长度200字节,如果不够,空位补空格;
(2)在包尾增加回车转行符进行分割,例如FTP协议;
(3)将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段,通常设计思路为消息头的第一个字段使用INT32来表示消息的总长度;
(4)更复杂的应用层协议。

 

26 java单例模式为什么一定要加上volatile关键字

https://www.jianshu.com/p/35e4504d42e4

要理解为什么要加volatile,首先要理解new Singleton()做了什么。new一个对象有几个步骤。1.看class对象是否加载,如果没有就先加载class对象,2.分配内存空间,初始化实例,3.调用构造函数,4.返回地址给引用。而cpu为了优化程序,可能会进行指令重排序,打乱这3,4这几个步骤,导致实例内存还没分配,就被使用了。

再用线程A和线程B举例。线程A执行到new Singleton(),开始初始化实例对象,由于存在指令重排序,这次new操作,先把引用赋值了,还没有执行构造函数。这时时间片结束了,切换到线程B执行,线程B调用new Singleton()方法,发现引用不等于null,就直接返回引用地址了,然后线程B执行了一些操作,就可能导致线程B使用了还没有被初始化的变量。

 

27 spring的理解

划重点IOC AOP重点说

1aop

  • 认证权限

    缓存缓存

    上下文传递内容传递

    错误处理错误处理

    懒加载懒加载

    调试调试

    记录,跟踪,分析和监控记录跟踪优化校准

    性能优化性能优化

    持久性持久化

    资源池资源池

    同步同步

    交易事务

2ioc控制反转

  •  

 

28锁和原子变量(并发包里面的)

原子变量

  • 的AtomicInteger和AtomicIntegerArray:基于整数类型
  • 的AtomicBoolean:基于布尔类型
  • 的AtomicLong和AtomicLongArray:基于长类型
  • 的AtomicReference和AtomicReferenceArray:基于引用类型


compareAndSet方法又被称为CAS,该方法调用取消保存的一个compareAndSwapInt方法,这个方法是本地的,我们看不到源码,但是我们需要知道该方法完成的一个目标:比较当前原子变量的值是否等于期望,如果是则将其修改为并更新返回真,否则直接返回假的。当然,这个操作本身就是原子的,较为底层的实现。

 

29阻塞队列

https://www.cnblogs.com/dolphin0520/p/3932906.html

 在前面几篇文章中,我们讨论了同步容器(哈希表,向量),也讨论了并发容器(ConcurrentHashMap中,的CopyOnWriteArrayList),这些工具都为我们编写多线程程序提供了很大的方便。今天我们来讨论另外一类容器:阻塞队列。
  在前面我们接触的队列都是非阻塞队列,比如PriorityQueue中,链表(链表是双向链表,它实现了出列接口)。
  使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提 供了极大的方便性。
  本文先讲述一下的java.util.concurrent包下提供主要的几种阻塞队列,然后分析了阻塞队列和非阻塞队列的中的各个方法,接着分析了阻塞队列的实现原理,最后给出了一个实际例子和几个使用场景。

 

几种阻塞队列

自从Java 1.5之后,在java.util.concurrent包下提供了若干个阻塞队列,主要有以下几个:

  ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。

  的LinkedBlockingQueue:基于链表实现的一个阻塞队列,在创建的LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE的。

  的PriorityBlockingQueue:以上2种队列都是先进先出队列,而的PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。

  DelayQueue:基于PriorityQueue中,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素.DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

阻塞队列中的方法VS非阻塞队列中的方法

1.非阻塞队列中的几个主要方法:
  add(E e):将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则会抛出异常;
  remove():移除队首元素,若移除成功,则返回true;如果移除失败(队列为空),则会抛出异常;
  offer(E e):将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则返回false;
  poll():移除并获取队首元素,若成功,则返回队首元素;否则返回null;
  peek():获取队首元素,若成功,则返回队首元素;否则返回空
  。对于非阻塞队列,一般情况下建议使用的报价,民意调查和偷看三个方法,不建议使用添加和删除方法因为使用的报价,民意调查和偷看三个方法可以通过返回值判断操作成功与否,而使用添加和删除方法却不能达到这样的效果。注意,非阻塞队列中的方法都没有进行同步措施。
2.阻塞队列中的几 主要方法:
  阻塞队列包括了非阻塞队列中的大部分方法,上面列举的5个方法在阻塞队列中都存在,但是要注意这5个方法在阻塞队列中都进行了同步措施除此之外。 ,阻塞队列提供了另外4个非常有用的方法:
  put(E e)
  take()
  offer(E e,long timeout,TimeUnit unit)
  poll(long timeout,TimeUnit unit)
  
  put方法用来向队尾存入元素,如果队列满,则等待;
  取方法用来从队首取元素,如果队列为空,则等待;
  提供方法用来向队尾存入元素,如果队列满,则等待一定的时间,当时间期限达到时,如果还没有插入成功,则返回false;否则返回true;
  poll方法用来从队首取元素,如果队列空,则等待一定的时间,当时间期限达到时,如果取到,则返回NULL;否则返回取得的元素;

源码分析看上面的网址

 

30 threadlocal

ThreadLocal的,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道的ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量

 

31内存泄漏和内存溢出

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak会最终会导致out of memory!

 

32数据库设计优化

1, 
逻辑设计阶段通过数据模型(ER模型,UML图例)得到数据概念模型 - >转换为SQL表 - >规范化 
3,物理设计阶段选择索引 
4,实现阶段把物理设计的结果转换为数据库管理系统(DBMS)中的DDL创建数据结构。通过SQL语句来进行增改改查 
.5,再设计再修改 
.6,废止

第三范式(其实有5个)

1NF表示表中的数据的每一个字段都是不可以再分割的原子数据。

2NF表示在满足1NF的情况下,表中的非码属性必须被联合主键决定,不能有联合主键中的某一个属性决定。例如:成绩表(课程号,学号,成绩,课程名)这个成绩表中的”课程名“这个属性只依赖课程号这一个属性,而不是依赖课程名和学号这个联合主键,所以这张表不满足2NF,需要将这张表分解为如下两张表才能满足2NF成绩表(课程号,学号,成绩)课程表(_课程号,课程名)

3NF表示在满足2NF的情况下,每一个非关键字必须由关键字来决定而不能由非关键字来决定。例如:信息表(_职工编号,职工姓名,部门名称,部门位置)这个信息表的主键是职工编号,但是部门经理名称并不依赖职工编号而是依赖与非主键的部门名,这样就不满足3NF了,可做如下修改职工表(_职工编号,职工姓名,部门编号)部门表(_部门编号,部门名称,部门位置)

 

33SpringMVC是怎么处理请求的

      1.用户向服务器发送请求,请求被Spring前端控制Servelt DispatcherServlet捕获;
      2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
      3. DispatcherServlet根据获得的处理程序,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
       4 。提取请求中的模型数据,填充Handler入参,开始执行Handler(Controller)。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
      HttpMessageConveter:将请求消息(如JSON,XML等数据)转换成一个对象,将对象转换为指定的响应信息
      数据转换:对请求消息进行数据转换如字符串转换成整数,双等
      据根式化:对请求消息进行数据格式化如将字符串转换成格式化数字或格式化日期等。
      数据验证:验证数据的有效性(长度,格式等),验证结果存储到BindingResult或错误中
      5 。Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象;
      6.根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet;
      7. ViewResolver结合模型和视图,来渲染视图
      8。将渲染结果返回给客户端。

https://www.cnblogs.com/dreamworlds/p/5396112.html  相关资料

 

34redis做缓存是做了些什么?

  Redis的整合shiro 

  登陆的信息通过subject存到session然后通过一些jar包,将会话整合到redis其实就只存了一些登陆的信息

  后来迁移到机房,多台服务器,就用cookie了,机密方式通过自己的JDK替换加密包改掉了

 

35堆和栈都用来存放什么的

堆主要用来存放对象的,栈主要是用来执行程序的。

在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,就以后可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象
引用变量相当于为数组或者对象起的一个别名,或者代号。

 

1,栈区(stack) - 由编译器自动分配释放,存放函数的参数值,局部变量的值等。

堆区(堆) - 一般由程序员分配释放,若程序员不释放,程序结束时可能由os回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

3,全局区(静态区)(静态) - 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。

4,文字常量区 - 常量字符串就是放在这里的。程序结束后由系统释放。

5,程序代码区 - 存放函数体的二进制代码。

 

36 mysql联合索引

https://www.cnblogs.com/zhima-hu/p/7323442.html

其实在联合索引上会有一个MySQL的索引最左匹配原则,但是如果联合索引的第一个列不在其中条件语句中,并且所查询的列其中有的是没有建立索引的,那么这个联合索引就是无效的

 

37 @Autowired的实现原理

 

AutowiredAnnotationBeanPostProcessor

当Spring容器启动时,AutowiredAnnotationBeanPostProcessor将扫描Spring容器中所有Bean,当发现Bean中拥有@Autowired注解时就找到和其匹配(默认按类型匹配)的Bean,并注入到对应的地方中去。源码分析如下:

 

39 springmvc和springboot的区别

spring是一个“引擎”;

Spring MVC是基于Spring的一个MVC框架;

Spring Boot是基于Spring4的条件注册的一套快速开发整合包。

 

40Set的性质

 

我们使用set的原因是set不包含重复元素,HashSet的,TreeSet中和LinkedHashSet三种类型什么时候使用它们,使用哪个这是一个很重要的选择性问题,正确的选择会大大提升程序运行效率;总结一下,如你的需求是要一个能快速访问的设置,那么就要用HashSet的,如果你要一个排序设置,那么你应该用TreeSet的,如果你要记录下插入时的顺序时,你应该使用LinedHashSet。把握这几个原则,是不是选择起来就简单多了。

设置接口的特性,设置接口继承了系列接口,设置集合中不能包含重复的元素,每个元素必须是唯一的,你只要将元素加入设置中,重复的元素会自动移除。下面分三方面对它的三个实现类进行说明。

 

41hashtable怎么实现线程安全

  得到和把方法都进行了同步的效率极其低下

 

42TCP/IP协议

TCP/IP 是一类协议系统,它是用于网络通信的一套协议集合.
传统上来说 TCP/IP 被认为是一个四层协议


1) 网络接口层:
主要是指物理层次的一些接口,比如电缆等.

2) 网络层:
提供独立于硬件的逻辑寻址,实现物理地址与逻辑地址的转换.

在 TCP / IP 协议族中,网络层协议包括 IP 协议(网际协议),ICMP 协议( Internet 互联网控制报文协议),以及 IGMP 协议( Internet 组管理协议).

3) 传输层:
为网络提供了流量控制,错误控制和确认服务.

在 TCP / IP 协议族中有两个互不相同的传输协议: TCP(传输控制协议)和 UDP(用户数据报协议).

4) 应用层:
为网络排错,文件传输,远程控制和 Internet 操作提供具体的应用程序

 

43 Java能否多继承? 

接口是常量值和方法定义的集合。接口是一种特殊的抽象类。
java类是单继承的。classB Extends classA
java接口可以多继承。Interface3 Extends Interface0, Interface1, interface……
 

不允许类多重继承的主要原因是,如果A同时继承B和C,而B和C同时有一个D方法,A如何决定该继承那一个呢?
但接口不存在这样的问题,接口全都是抽象方法继承谁都无所谓,所以接口可以继承多个接口。
注意:
1)一个类如果实现了一个接口,则要实现该接口的所有方法。
2)方法的名字、返回类型、参数必须与接口中完全一致。如果方法的返回类型不是void,则方法体必须至少有一条return语句。
3)因为接口的方法默认是public类型的,所以在实现的时候一定要用public来修饰(否则默认为protected类型,缩小了方法的使用范围)。

 

44 有没有试过多线程模拟并发? 

java的CountDownLatch

 

45 查看进程

linux ps -ef|grep java

46 TCP和UDP的区别

TCP的优点: 可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。 TCP的缺点: 慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。 而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。

UDP的优点: 快,比TCP稍安全 UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。但UDP也是无法避免攻击的,比如:UDP Flood攻击…… UDP的缺点: 不可靠,不稳定 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。 基于上面的优缺点,那么: 什么时候应该使用TCP: 当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。 在日常生活中,常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输 ………… 什么时候应该使用UDP: 当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 比如,日常生活中,常见使用UDP协议的应用如下: QQ语音 QQ视频 TFTP ……

有些应用场景对可靠性要求不高会用到UPD,比如长视频,要求速率

小结TCP与UDP的区别:

1.基于连接与无连接;
2.对系统资源的要求(TCP较多,UDP少);
3.UDP程序结构较简单;
4.流模式与数据报模式 ;

5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。

 

tcp协议和udp协议的差别 
TCP UDP 
是否连接 面向连接 面向非连接 
传输可靠性 可靠 不可靠 
应用场合 传输大量数据 少量数据 
速度 慢 快

TCP与UDP区别总结:

1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的

UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP首部开销20字节;UDP的首部开销小,只有8个字节

6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

 

47 socket是啥? socket传输怎么实现的?

建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

 

48 那些可以作为GC ROOT

在Java虚拟机中判断一个对象是否可以被回收,有一种做法叫可达性分析算法,也就是从GC Root到各个对象,如果GC Root到某个对象还有可达的引用链,那么这个对象就还不能被回收,否则就等着被收割吧。

这里既然提到了GC Root,那么哪类对象可以作为GC Root呢,这是一个在面试中经常被问到的问题。

《深入理解Java虚拟机》一书中是这么说的,一下几种对象可以作为GC Root:

虚拟机栈中的引用对象
方法区中类静态属性引用的对象
方法区中常量引用对象
本地方法栈中JNI引用对象
大概就是以上几种,笔者记性不好,看了好几次都记不住,好几次面试都在这个问题上栽了跟头。不说了,都是泪,/(ㄒoㄒ)/~~

 

49 LinkedHashMap

 HashMap和双向链表合二为一即是LinkedHashMap。所谓LinkedHashMap,其落脚点在HashMap,因此更准确地说,它是一个将所有Entry节点链入一个双向链表的HashMap。由于LinkedHashMap是HashMap的子类,所以LinkedHashMap自然会拥有HashMap的所有特性。比如,LinkedHashMap的元素存取过程基本与HashMap基本类似,只是在细节实现上稍有不同。当然,这是由LinkedHashMap本身的特性所决定的,因为它额外维护了一个双向链表用于保持迭代顺序。此外,LinkedHashMap可以很好的支持LRU算法,笔者在第七节便在LinkedHashMap的基础上实现了一个能够很好支持LRU的结构。
 

 

50spring中各种context有什么功能,他们之间有什么联系?

第一步:

 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;

第二步:

 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以【WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE】为属性Key,将其存储到ServletContext中,便于获取;

第三步:

 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用【WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE】先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文(有个parent属性作为对Spring的ApplicationContext的引用)。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是XmlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

通过上面这个Spring的启动过程,我们可以清楚的了解ServletContext、WebApplicationContext、XmlWebApplicationContext、以及DispatcherServlet上下文之间的关系,并且会将WebApplicationContext放在ServletContext中。

https://www.jianshu.com/p/2537e2fec546

 

51介绍一下Syncronized锁。如果用这个关键字修饰一个静态方法,锁住了什么?如果修饰成员方法,锁住了什么?

修饰普通的类的话就是锁的对象

如果修饰静态方法的话,锁的就是类,俗称类锁

 

52volatile关键字的两层语义

https://www.cnblogs.com/dolphin0520/p/3920373.html

  一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

  1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

  2)禁止进行指令重排序。

53多线程中的i++线程安全吗?为什么?

先说不是原子的,因为这个是分为三步,读值,+1,写值。在这三步任何之间都可能会有CPU调度产生,造成i的值被修改,造成脏读脏写。
接下来说volatile不能解决这个线程安全问题。因为volatile只能保证可见性,不能保证原子性。回答这个只为了让面试官晓得你考虑周全,知识面广。
接下来说可以用锁。使用synchronized或者ReentrantLock都可以解决这个问题。这里还可以比较下这两种方式的优劣。教科书式的比较结束后,来一句“我认为一般使用synchronized更好,因为JVM团队一直以来都在优先改进这个机制,可以尽早获得更好的性能,并且synchronized对大多数开发人员来说更加熟悉,方便代码的阅读”。
最后补上AtomicInteger。为什么AtomicInteger使用CAS完成?因为传统的锁机制需要陷入内核态,造成上下文切换,但是一般持有锁的时间很短,频繁的陷入内核开销太大,所以随着机器硬件支持CAS后,JAVA推出基于compare and set机制的AtomicInteger,实际上就是一个CPU循环忙等待。因为持有锁时间一般较短,所以大部分情况CAS比锁性能更优。
最初是没有CAS,只有陷入内核态的锁,这种锁当然也需要硬件的支持。后来硬件发展了,有了CAS锁,把compare 和 set 在硬件层次上做成原子的,才有了CAS锁。

 

54synchronized关键字和Lock的区别

synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢?
  在上面一篇文章中,我们了解到如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
  1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
  2)线程执行发生异常,此时JVM会让线程自动释放锁。
  那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。
  因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。
  再举个例子:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。
  但是采用synchronized关键字来实现同步的话,就会导致一个问题:
  如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。
  因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。
  另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。
  总结一下,也就是说Lock提供了比synchronized更多的功能。但是要注意以下几点:
  1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;
  2)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

 

55原子类和CAS

https://www.cnblogs.com/zhengbin/p/5657707.html#autoid-0-1-0

1 volatile  修饰value值 保证可见性

2 incrementAndGet  不断做compareAndSet 操作,compareAndSet()方法调用的compareAndSwapInt()方法的声明如下,是一个native方法。 

 

56长连接怎么实现的

基于HTTP的长连接,是一种通过长轮询方式实现”服务器推”的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性。

长连接、长轮询:都是通过不断的向服务器发出请求,如果服务器有数据则马上返回,如果没有数据就会hold住请求,等到有数据的时候就推送给页面。

通常的做法是,在服务器的程序中加入一个死循环,在循环中监测数据的变动。当发现新数据时,立即将其输出给浏览器并断开连接,浏览器在收到数据后,再次发起请求以进入下一个周期,这就是常说的长轮询(long-polling)方式。

https://blog.csdn.net/lcc793385991/article/details/79200358

 

57聚簇索引和非聚簇索引

https://www.cnblogs.com/aspwebchh/p/6652855.html

非聚集索引和聚集索引的区别在于, 通过聚集索引可以查到需要查找的数据, 而通过非聚集索引可以查到记录对应的主键值 , 再使用主键的值通过聚集索引查找到需要的数据

 

不管以任何方式查询表, 最终都会利用主键通过聚集索引来定位到数据, 聚集索引(主键)是通往真实数据所在的唯一路径。

 

58 介绍一下spring

sping 是一个框架开发的容器,
它是为了可以良好的控制程序而开发的;

由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式;

核心容器(Spring core)

核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。BeanFactory使用依赖注入的方式提供给组件依赖。

Spring上下文(Spring context)

Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。

Spring面向切面编程(Spring AOP)

通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。


Spring DAO模块

DAO模式主要目的是将持久层相关问题与一般的的业务规则和工作流隔离开来。Spring 中的DAO提供一致的方式访问数据库,不管采用何种持久化技术,Spring都提供一直的编程模型。Spring还对不同的持久层技术提供一致的DAO方式的异常层次结构。

Spring ORM模块

Spring 与所有的主要的ORM映射框架都集成的很好,包括Hibernate、JDO实现、TopLink和IBatis SQL Map等。Spring为所有的这些框架提供了模板之类的辅助类,达成了一致的编程风格。

Spring Web模块

Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。Web层使用Web层框架,可选的,可以是Spring自己的MVC框架,或者提供的Web框架,如Struts、Webwork、tapestry和jsf。

Spring MVC框架(Spring WebMVC)

MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。Spring的MVC框架提供清晰的角色划分:控制器、验证器、命令对象、表单对象和模型对象、分发器、处理器映射和视图解析器。Spring支持多种视图技术。

59策略模式

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。  比如加减乘除

 

60 单例模式的两种形式  以及double-check lock

https://www.cnblogs.com/x_wukong/p/3962315.html

 

61 redis注意事项

数据不一致问题。不要缓存一致性很高的数据

当更新数据时,如更新某商品的库存,当前商品的库存是100,现在要更新为99,先更新数据库更改成99,然后删除缓存,发现删除缓存失败了,这意味着数据库存的是99,而缓存是100,这导致数据库和缓存不一致。

方案:(删除缓存)

这种情况应该是先删除缓存,然后在更新数据库,如果删除缓存失败,那就不要更新数据库,如果说删除缓存成功,而更新数据库失败,那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。

 

62 线程交互

https://blog.csdn.net/carson0408/article/details/79430813

     线程交互是指两个线程之间通过通信联系对锁的获取与释放,从而达到较好的线程运行结果,避免引起混乱的结果。一般来说synchronized块的锁会让代码进入同步状态,即一个线程运行的同时让其它线程进行等待,那么如果需要进行实现更复杂的交互,则需要学习以下几个方法:

void notify():唤醒在此对象监视器上等待的单个线程。

void notifyAll():唤醒在此对象监视器上等待的所有线程。

void wait():让占用了这个同步对象的线程,临时释放当前的占用,并且等待。

 

63 重载和重写

重载就传过来不同的参数类型和个数,调用不同的方法  这个就叫是多态性

重写就是对父类方法的重写  抽象类一定要重写  但是final类不能被重写

 

64面向对象三大特性

继承,多态,封装。

 

65java类加载机制

http://www.importnew.com/25295.html

如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。

加载

加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个Class文件获取,这里既可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类)。

验证

这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备

准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。注意这里所说的初始值概念,比如一个类变量定义为:

1

public static int v = 8080;

实际上变量v在准备阶段过后的初始值为0而不是8080,将v赋值为8080的putstatic指令是程序被编译后,存放于类构造器<client>方法之中,这里我们后面会解释。
但是注意如果声明为:

1

public static final int v = 8080;

在编译阶段会为v生成ConstantValue属性,在准备阶段虚拟机会根据ConstantValue属性将v赋值为8080。

解析

解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是class文件中的:

  • CONSTANT_Class_info
  • CONSTANT_Field_info
  • CONSTANT_Method_info

等类型的常量。

下面我们解释一下符号引用和直接引用的概念:

  • 符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
  • 直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。

初始化

初始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码。

初始化阶段是执行类构造器<client>方法的过程。<client>方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证<client>方法执行之前,父类的<client>方法已经执行完毕。p.s: 如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成<client>()方法。

注意以下几种情况不会执行类初始化:

  • 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
  • 定义对象数组,不会触发该类的初始化。
  • 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
  • 通过类名获取Class对象,不会触发类的初始化。
  • 通过Class.forName加载指定类时,如果指定参数initialize为false时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
  • 通过ClassLoader默认的loadClass方法,也不会触发初始化动作。

类加载器

虚拟机设计团队把加载动作放到JVM外部实现,以便让应用程序决定如何获取所需的类,JVM提供了3种类加载器:

  • 启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
  • 扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
  • 应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。

JVM通过双亲委派模型进行类的加载,当然我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。

当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。

采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。

在有些情境中可能会出现要我们自己来实现一个类加载器的需求,由于这里涉及的内容比较广泛,我想以后单独写一篇文章来讲述,不过这里我们还是稍微来看一下。我们直接看一下jdk中的ClassLoader的源码实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

protected synchronized Class<?> loadClass(String name, boolean resolve)

        throws ClassNotFoundException {

    // First, check if the class has already been loaded

    Class c = findLoadedClass(name);

    if (c == null) {

        try {

            if (parent != null) {

                c = parent.loadClass(name, false);

            } else {

                c = findBootstrapClass0(name);

            }

        } catch (ClassNotFoundException e) {

            // If still not found, then invoke findClass in order

            // to find the class.

            c = findClass(name);

        }

    }

    if (resolve) {

        resolveClass(c);

    }

    return c;

}

  • 首先通过Class c = findLoadedClass(name);判断一个类是否已经被加载过。
  • 如果没有被加载过执行if (c == null)中的程序,遵循双亲委派的模型,首先会通过递归从父加载器开始找,直到父类加载器是Bootstrap ClassLoader为止。
  • 最后根据resolve的值,判断这个class是否需要解析。

而上面的findClass()的实现如下,直接抛出一个异常,并且方法是protected,很明显这是留给我们开发者自己去实现的,这里我们以后我们单独写一篇文章来讲一下如何重写findClass方法来实现我们自己的类加载器。

1

2

3

protected Class<?> findClass(String name) throws ClassNotFoundException {

    throw new ClassNotFoundException(name);

}

References

UNDERSTANDING THE JVM

 

66 HashMap扩容之后为什么需要重新计算hash值

hashMap是一个存储key-value键值对的集合,每个键值对称为一个entity,这些entity分散存储到一个数组中,这个数组就是hashMap的主干,我们通过一个hash函数得到一列二进制数去与(数组长度-1)去&,得到index数组下标位置,然后put数组中去,当我们长度改变,肯定要重新计算hash值
https://blog.csdn.net/qq_37154446/article/details/80059468

 

67Minor GC ,Major GC, Full Gc

大家应该注意到,目前,这些术语无论是在 JVM 规范还是在垃圾收集研究论文中都没有正式的定义。但是我们一看就知道这些在我们已经知道的基础之上做出的定义是正确的,Minor GC 清理年轻带内存应该被设计得简单:

  • Major GC 是清理永久代。
  • Full GC 是清理整个堆空间—包括年轻代和永久代。

https://www.cnblogs.com/yang-hao/p/5948207.html

 

68Mybatis的一二级缓存

MyBatis的缓存分为两种:

  1. 一级缓存,一级缓存是SqlSession级别的缓存,对于相同的查询,会从缓存中返回结果而不是查询数据库
  2. 二级缓存,二级缓存是Mapper级别的缓存,定义在Mapper文件的<cache>标签中并需要开启此缓存,多个Mapper文件可以共用一个缓存,依赖<cache-ref>标签配置

https://www.cnblogs.com/xrq730/p/6991655.html

 

69MyISAM和InnoDB的区别

1、MyISAM:默认表类型,它是基于传统的ISAM类型,ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法。不是事务安全的,而且不支持外键,如果执行大量的select,insert MyISAM比较适合。

2、InnoDB:支持事务安全的引擎,支持外键、行锁、事务是他的最大特点。如果有大量的update和insert,建议使用InnoDB,特别是针对多个并发和QPS较高的情况。

https://www.cnblogs.com/y-rong/p/8110596.html

 

70 Redis使用场景

看视频

 

71@Autowired和@Resource的区别?

https://www.cnblogs.com/think-in-java/p/5474740.html

 

Spring注解@Resource和@Autowired区别对比

@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

1、共同点

两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

2、不同点

(1)@Autowired

@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

复制代码

public class TestServiceImpl {
    // 下面两种@Autowired只要使用一种即可
    @Autowired
    private UserDao userDao; // 用于字段上
    
    @Autowired
    public void setUserDao(UserDao userDao) { // 用于属性的方法上
        this.userDao = userDao;
    }
}

复制代码

@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:

public class TestServiceImpl {
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao; 
}

(2)@Resource

@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

复制代码

public class TestServiceImpl {
    // 下面两种@Resource只要使用一种即可
    @Resource(name="userDao")
    private UserDao userDao; // 用于字段上
    
    @Resource(name="userDao")
    public void setUserDao(UserDao userDao) { // 用于属性的setter方法上
        this.userDao = userDao;
    }
}

复制代码

注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。

@Resource装配顺序:

①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。

②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。

④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。

@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。

 

72CAS的缺点

https://blog.csdn.net/v123411739/article/details/79561458

CAS虽然很高效的解决了原子操作问题,但是CAS仍然存在三大问题。

  1. 循环时间长开销很大。
  2. 只能保证一个共享变量的原子操作。
  3. ABA问题。

 

73动态代理

按照代理的创建时期,代理类可以分为两种: 

静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

动态:在程序运行时运用反射机制动态创建而成。

https://www.cnblogs.com/baizhanshi/p/6611164.html

 

74stringbuffer是否安全

https://blog.csdn.net/fireofjava/article/details/8636870

我们知道StringBuffer是线程安全的,StringBuilder是非线程安全的,但是这个安全并不是绝对的。

    StringBuffer的方法都加了synchronized关键字来保证每一次方法调用都是线程安全的,但是如果多个线程同时调用StringBuffer的append的方法仍然是有问题的

 

75 RabbitMQ

ConnectionFactory、Connection、Channel,这三个都是RabbitMQ对外提供的API中最基本的对象。不管是服务器端还是客户端都会首先创建这三类对象。
     ConnectionFactory为Connection的制造工厂。

Connection是与RabbitMQ服务器的socket链接,它封装了socket协议及身份验证相关部分逻辑。

Channel是我们与RabbitMQ打交道的最重要的一个接口,大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。

1  RabbitMQ发送消息一端

    1 factory建立    
    ConnectionFactory factory = new ConnectionFactory();
    2  设置RabbitMQ所在主机ip以及主机名
    factory .setHost("127.0.0.1");
    3 创建一个连接
    Connection  conection = factory.newConnection();
    4 创建一个管道
    Channel channel = connection.createChannel();
    5 指定一个队列
    channel.queueDeclare(QUEUE_NAME,false,false,false,null)
    6 发送消息
    String message=“你好”;
    channel.basicPublish("",QUEUE_NAME,null,message.getByte());
    7管道管理连接关闭
    channel.close();
    connection.close();

 

2 RabbitMQ接受消息一端
    1 factory建立    
    ConnectionFactory factory = new ConnectionFactory();
    2 打开连接和创建频道,与发送端一样
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();
    3 声明队列,主要为了防止消息接收者先运行此程序,队列还不存在时创建队列。
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);
    4 创建队列消费者
    QueueingConsumer consumer = new QueueingConsumer(channel);
    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
    5 指定消费队列
    channel.basicConsume(QUEUE_NAME, true, consumer);
    while (true) {
        // nextDelivery是一个阻塞方法(内部实现其实是阻塞队列的take方法)
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [x] Received '" + message + "'");
    }

 

一定要先声明队列
作队列之消息分发机制
使用Consumer consumer = new DefaultConsumer(channel) 
override  handelDelivery

公平分发
channel.basicQos(1);//保证一次只分发一个
为了解决这个问题,我们使用basicQos( prefetchCount = 1)方法,
来限制RabbitMQ只发不超过1条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发送。

转发器的概念要知道
Exchange
 channel.queueBind(queueName, EXCHANGE_NAME, "");

 

76 dubbo的原理

Dubbo提供了三个关键功能:基于接口的远程调用,容错与负载均衡,服务自动注册与发现。

Dubbo使得调用远程服务就像调用本地java服务一样简单。

1) 暴露服务到本地

2) 暴露服务到远程

3) 启动netty服务

4) 连接zookeeper

5) 注册服务到zookeeper

6) 监听zookeeper中消费服务

Dubbo支持多种协议,如下所示:

  • Dubbo协议
  • Hessian协议
  • HTTP协议
  • RMI协议
  • WebService协议
  • Thrift协议
  • Memcached协议
  • Redis协议

 

77 数据结构---树

B树 B+树  

 

78   接口和抽象类的选择

  1. 如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。
  2. 如果知道某个类应该是基类,那么第一个选择的应该是让它成为一个接口,只有在必须要有方法定义和成员变量的时候,才应该选择抽象类。因为抽象类中允许存在一个或多个被具体实现的方法,只要方法没有被全部实现该类就仍是抽象类。

 

79 jdk动态代理原理

  

扩展:JDK 动态代理如何实现?(加分点)

答:JDK 动态代理,只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。

  1. 定义一个实现接口 InvocationHandler 的类;
  2. 通过构造函数,注入被代理类;
  3. 实现 invoke( Object proxy, Method method, Object[] args)方法;
  4. 在主函数中获得被代理类的类加载器;
  5. 使用 Proxy.newProxyInstance( ) 产生一个代理对象;
  6. 通过代理对象调用各种方法。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值