Java继承关系初始化顺序
父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类的非静态变量(子类的非静态代码块)-->子类的构造函数
网络攻击
sql注入攻击 SQL注入攻击中以SQL语句作为用户输入,从而达到查询/修改/删除数据的目的
xxs攻击 xss表示Cross Site Scripting(跨站脚本攻击),通过插入恶意脚本(比如js脚本),实现对用户游览器的控制
Minor GC ,Full GC 触发条件
Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法去空间不足
(4)升到老年代的对象大于老年代剩余空间
降低GC的调优的策略:调优诸如通过NewRatio控制新生代老年代比例,通过 MaxTenuringThreshold控制进入老年前生存次数。
mybatis一级缓存,二级缓存
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。
用户发起查询请求,查找某条数据,sqlSession先去缓存中查找,是否有该数据,如果有,读取;
如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。
但sqlSession执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读。
二级缓存是全局缓存,作用域是针对Mapper的Namespace而言的,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
spring-mybatis处理事务配置
如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager
1.你需要使用如下的XML将其装配到应用程序的上下文定义中,添加注解配置:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--使用注释事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
2.在需要加入事务的方法或者类上添加@Transactional
事物配置中有哪些属性可以配置
(1).事务的传播性:@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
(2).事务的超时性:@Transactional(timeout=30) //默认是30秒
注意这里说的是事务的超时性而不是Connection的超时性,这两个是有区别的
(3).事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
(4).回滚:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。
(5).只读:@Transactional(readOnly=true)
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。
object类的基本方法
getClass(), hashCode(), equals(), clone(), toString(), notify(), notifyAll(), wait(), finalize()
mybatis分页原理解析
https://blog.csdn.net/hupanfeng/article/details/9265341
https://blog.csdn.net/hupanfeng/article/details/9238127
sql的解析是在StatementHandler里完成的,所以为了重写sql需要拦截StatementHandler,sql重写其实在原始的sql语句上加入分页的参数.
redis sortedMap的跳表结构
https://blog.csdn.net/sinat_35261315/article/details/62890796
1.链表从头节点到尾节点是有序的
2.可以进行跳跃查找(形如二分法)
concurrent并发包中的类
BlockingQueue: 通常用于一个线程生产对象,而另外一个线程消费这些对象的场景。
ConcurrentHashMap:不会锁住整个 Map。它的内部只是把 Map 中正在被写入的部分进行锁定。能够提供比 HashTable 更好的并发性能。
闭锁 CountDownLatch:允许一个或多个线程等待一系列指定操作的完成。CountDownLatch 以一个给定的数量初始化。countDown() 每被调用一次,这一数量就减一。通过调用 await() 方法之一,线程可以阻塞等待这一数量到达零。
栅栏 CyclicBarrier:是一种同步机制,它能够对处理一些算法的线程实现同步。换句话讲,它就是一个所有线程必须等待的一个栅栏,直到所有线程都到达这里,然后所有线程才可以继续做其他事情。
信号量 Semaphore:1.保护一个重要(代码)部分防止一次超过 N 个线程进入。2.在两个线程之间发送信号。
锁 Lock:
lock() 将 Lock 实例锁定。如果该 Lock 实例已被锁定,调用 lock() 方法的线程将会阻塞,直到 Lock 实例解锁。
tryLock() 不带参数的方法试图立即锁定 Lock 实例。如果锁定成功,它将返回 true,如果 Lock 实例已被锁定该方法返回 false。这一方法永不阻塞。tryLock(long timeout, TimeUnit timeUnit) 的工作类似于 tryLock() 方法,除了它在放弃锁定 Lock 之前等待一个给定的超时时间之外。
unlock() 方法对 Lock 实例解锁。一个 Lock 实现将只允许锁定了该对象的线程来调用此方法。其他(没有锁定该 Lock 对象的线程)线程对 unlock() 方法的调用将会抛一个未检查异常(RuntimeException)。
原子性整型 AtomicInteger:提供了一个可以进行原子性读和写操作的 int 变量。
用户态和内核态的区别
内核态与用户态是操作系统的两种运行级别,当程序运行在3级特权级上时,就可以称之为运行在用户态,因为这是最低特权级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态;反之,当程序运行在0级特权级上时,就可以称之为运行在内核态。运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态。
以下三种情况会导致用户态到内核态的切换:
这两种状态的主要差别是: 处于用户态执行时,进程所能访问的内存空间和对象受到限制,其所处于占有的处理机是可被抢占的 ; 而处于核心态执行中的进程,则能访问所有的内存空间和对象,且所占有的处理机是不允许被抢占的。
1)系统调用
用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作
2)异常
当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常
3)外围设备的中断
当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。
cookie和session生命周期
cookie在不设置过期时间的情况下浏览器关闭后就失效了,如果设置了cokie的过期时间.那么浏览器会把cookie保存到硬盘中,再次打IE时会依然有效.直到超过设置的有效期。
session的生命周期是间隔的,从创建时,开始计时如在20分钟,没有访问session,那么session生命周期被销毁。但是,如果在20分钟内(如在第19分钟时)访问过session,那么,将重新计算session的生命周期。因为保存Sessionid的cookie是临时的(浏览器关闭就会销毁),所以我们自己手动创建一个持久化的cookie,用来保存sessionid。
Dubbo的实现
首先ServiceConfig类拿到对外提供服务的实际类ref(如:HelloWorldImpl),然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,
到这一步就完成具体服务到Invoker的转化。接下来就是Invoker转换到Exporter的过程
Dubbo协议的Invoker转为Exporter发生在DubboProtocol类的export方法,它主要是打开socket侦听服务,并接收客户端发来的各种请求,通讯细节由Dubbo自己实现。
Dubbo缺省协议采用单一长连接和NIO异步通讯,底层是socket长连接。
n个数中最小的k个数
1.排序找前k个
2.局部替换法,假设前k个就最小,建立最大堆,比堆顶小就替换然后调整堆
3.数据量过大,可以先分组,然后每个组用2的方法,最后多路归并
虚拟内存和物理内存
物理内存就是咱们通常值得内存条的容量。而虚拟内存是指我们将硬盘空间划分出来一部分,用于与内存同样的作用。如果物理内存占的多了,会表现出系统反映缓慢等问题。而虚拟内存多了,只会占用硬盘空间。
i++,++i在java中并不是线程安全的
java中若要在多线程情况下使得线程安全要加锁,或者用atomicInteger类,这个类的线程安全是由volatile变量实现的,使得每次都从共享内存读取值。
String、StringBuffer与StringBuilder的区别
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,StringBuilder是线程不安全的,而StringBuffer是线程安全的。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
hashcode()的作用
注意:hashCode() 在散列表中才有用,在其它情况下没用。
1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
2、如果两个对象相同,就是适用于equals(Java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点
4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
java中的四种引用
强引用,软引用,弱引用,虚引用
Java中提供这四种引用类型主要有两个目的:
第一是可以让程序员通过代码的方式决定某些对象的生命周期;
第二是有利于JVM进行垃圾回收。
强引用有引用变量指向时永远不会被垃圾回收,JVM宁愿抛出OutOfMemory错误也不会回收这种对象
软引用:如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它
弱引用:无论内存是否充足,都会回收被弱引用关联的对象
虚引用:并不影响对象的生命周期,跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收
redis线程模型
redis采用单线程模型,到达服务端,所有的命令都会进入一个队列中,然后逐个被执行。1.redis是纯内存访问,数据存放在内存中,内存的响应时间极快,2.采用epoll做为I/O多路复用技术的实现,不在I/O上浪费过多的时间,3.单线程避免了线程切换和竞态产生的消耗,所以性能很高。
LinkedHashMap如何保证有序
LinkedHashMap具有可预知的迭代顺序,在hashmap的基础上采用双向链表的结构,根据链表中元素的顺序可以分为:按插入顺序的链表,和按访问顺序(调用get方法)的链表,accessOrder为true时,按访问顺序排序(实现了实现LRU),false时,按插入顺序排序。
在使用put方法时,key已存在,替换value(同HashMap),并调用recordAccess方法,方法作用为根据accessOrder的值保持链表顺序不变或者将将访问的当前节点移到链表尾部(头结点的前一个节点)。
key不存在,添加新的Entry,仍然是Table[i]= newEntry,旧链表首个为newEntry.next(同HashMap),将newEntry加到双向链表末尾(即header前,这样就保留了插入顺序)
mysql实现事务的原理
多版本和快照隔离(mvcc)
MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低,虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行.
MVCC的实现是通过保存数据在某个时间点的快照来实现的,也就是说,不管需要执行多长时间,只要事务开始时间相同,每个事务看到的数据都是一致的,事务开始的时间不同时,每个事务对同一张表,同一时刻看到的数据可能是不一样的(因为不同的时间点可能数据就已经产生了不同的快照版本,而每个事务在默认的RR隔离级别下只能看到事务开始时的数据快照)
innodd的mvcc是通过在每行记录后面保存两个隐藏列来实现的.这两个列,一个保存了行的创建时间.一个保存了行的过期时间(删除时间).列里面存储的并不是实际的时间值.而是系统版本号.每开启一个新的事务,系统版本号都会自动递增.
网络编程
传输层的拥塞控制有:慢开始、拥塞避免、快重传、快恢复
多线程如何同步:临界区、互斥区、信号量、事件
临界区:通过多线程串行化来访问公共资源,速度快
互斥区:拥有互斥对象的线程才具有访问资源的权限
信号量:允许多个线程同时访问资源,但需要限制最大线程数目,通过PV操作
事件:用来通知线程有一些事件已经发生,从而启动后继任务的开始
进程间通讯方式
- 管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
- 命名管道FIFO:这也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
- 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
- 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同进程间的进程通信。
异步:发送一个请求,不等待返回,随时发送下一个请求
Clone()方法
clone方法是一个浅拷贝。虽然是浅拷贝,但是拷贝生成的对象是一个新的对象,对拷贝对象的基本类型属性进行操作是不会影响原对象的基本类型属性的,如果是对拷贝对象的引用类型属性进行操作,是会影响到原对象的引用类型属性的。
异常分类
受查异常即IOException要在方法里用try..catch代码块捕获,而未检查异常(error和RuntimeException)不需要try…catch…或throws 机制去处理
10亿个数中找出最大的1000个数
先拿1000个数建堆,然后一次添加剩余元素,如果大于堆顶的数(1000中最小的),将这个数替换堆顶,并调整结构使之仍然是一个最小堆,这样,遍历完后,堆中的1000个数就是所需的最大的1000个。建堆时间复杂度是O(mlogm),算法的时间复杂度为O(nmlogm)(n为10亿,m为1000)。
优化的方法:可以把所有10亿个数据分组存放,比如分别放在1000个文件中。这样处理就可以分别在每个文件的10^6个数据中找出最大的1000个数,合并到一起在再找出最终的结果
提取出某日访问百度次数最多的那个IP
首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法,比如模1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map进行频率统计,然后再找出频率最大的几个)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。
或者如下阐述:
算法思想:分而治之+Hash
1、IP地址最多有2^32=4G种取值情况,所以不能完全加载到内存中处理;
2、可以考虑采用分而治之的思想,按照IP地址的Hash(IP) % 1024值,把海量IP日志分别存储到1024个小文件中,这样,每个小文件最多包含4MB个IP地址;
这里解释一下为什么用Hash(IP) % 1024值,如果不用,而直接分类的话,可能会出现这样一种情况,就是有个IP在每个小文件中都存在,而且这个IP并不一定在那个小文件中是数量最多的,那么最终可能选择的结果会有问题,所以这里用了Hash(IP)%1024值,这样的话,通过计算IP的Hash值,相同IP肯定会放到一个文件中,当然了不同的IP的Hash值也可能相同,就存在一个小文件中。
3、对于每一个小文件,可以构建一个IP为key,出现的次数为value的Hash Map,同时记录当前出现次数最多的那个IP地址;
4、可以得到1024个小文件中的出现次数最多的那个IP,再依据常规的排序算法得出总体上出现次数最多的IP。
索引采用B+树而不是红黑树?
操作系统读写磁盘的基本单位是扇区,而文件系统的基本单位是簇(Cluster)。
也就是说,磁盘读写有一个最少内容的限制,即使我们只需要这个簇上的一个字节的内容,我们也要含着泪把一整个簇上的内容读完。一个父节点只有 2 个子节点,并不能填满一个簇上的所有内容啊?那多余的内容岂不是要浪费了?我们怎么才能把浪费的这部分内容利用起来呢?哈哈,答案就是 B+ 树。
由于 B+ 树分支比二叉树更多,所以相同数量的内容,B+ 树的深度更浅,深度代表什么?代表磁盘 io 次数啊!数据库设计的时候 B+ 树有多少个分支都是按照磁盘一个簇上最多能放多少节点设计的啊!
所以,涉及到磁盘上查询的数据结构,一般都用 B+ 树啦。
JVM怎么识别class文件
每个class文件的头4个字节称为魔数(Magic Number),其值为:0xCAFEBABE,它的唯一作用是用于确定这个文件是否为一个能被虚拟机接受的class文件。使用魔数而不是扩展名来进行识别主要是基于安全的考虑,因为文件的扩展名可以随意地被改动。
Hashmap多线程并发会发生什么问题
HashMap在多线程put后可能导致get无限循环
ConcurrentHashmap
ConcurrentHashMap中包括了“Segment(锁分段)数组”,Segment包含了“HashEntry数组”,而“HashEntry数组”中的每一个HashEntry元素是一个单向链表。 Segment类继承于ReentrantLock类,所以Segment本质上是一个可重入的互斥锁.
对于ConcurrentHashMap的添加,删除操作,在操作开始前,线程都会获取Segment的互斥锁;操作完毕之后,才会释放。而对于读取操作,它是通过volatile去实现的,HashEntry数组是volatile类型的,而volatile能保证“即对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入”,即我们总能读到其它线程写入HashEntry之后的值。 以上这些方式,就是ConcurrentHashMap线程安全的实现原理。
1.7 1.8区别
1.抛弃了Segment分段锁机制,利用CAS(compare and swap)机制+Synchronized来保证并发更新的安全
2.底层采用数组+链表+红黑树的存储结构
Select,poll和epoll区别
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的.
select的几大缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低
(3)select最大的缺陷就是单个进程所打开的FD是有一定限制的,默认值是1024。
poll本质上和select没有区别,它只是没有最大连接数的限制,原因是它是基于链表来存储的。
epoll解决了上述问题:
1、没有最大并发连接的限制,能打开的FD的上限远大于1024
2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。
3、内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。
正向代理和反向代理?
正向代理:
客户端向代理发请求,代理向原始服务器转交请求并将获得的内容返回给客户端。
反向代理:
反向代理就是屏蔽内网服务器,让客户端访问反向代理服务器的外网IP,负载均衡的实现就是通过反向代理。
为什么内存分为堆和栈?
变量主要是两种形式,一种内容短小(比如一个int整数),需要频繁访问,但是生命周期很短,通常只在一个方法内存活,而另一种内容可能很多(比如很长一个字符串),可能不需要太频繁的访问,但生命周期较长,通常很多个方法中可能都要用到,那么自然将这两类变量分开就显得比较理性,一类存储在栈区,通常是局部变量、操作符栈、函数参数传递和返回值,另一类存储在堆区,通常是较大的结构体(或者OOP中的对象)、需要反复访问的全局变量
inode是什么?
文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。
inode包含文件的元信息,具体来说有以下内容:
* 文件的字节数
* 文件拥有者的User ID
* 文件的Group ID
* 文件的读、写、执行权限
* 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
* 链接数,即有多少文件名指向这个inode
* 文件数据block的位置
如果频繁老年代回收怎么分析解决
1.是否创建过大的对象及数组,这些会直接放入老年代
2.当系统中要加载的类、反射的类和调用的方法较多时,方法区可能会被占满,在未配置为采用CMS GC的情况下也会执行Full GC,应转为使用CMS GC。
3.注意GC日志中是否有promotion failed和concurrent mode failure两种状况,promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入老年代,而此时老年代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的。措施:增大survivor space。
自己设计一个数据库连接池怎么设计
使用LinkedList集合存放数据库连接,实现javax.sql.DataSource接口。
JVM内存结构划分
如上图,一共分为五块,其中:
线程共享区域为:
1、java堆
2、方法区
线程私有区域为:
3、JVM栈
4、本地方法栈
5、程序计数器
堆内存是JVM最大的一块由年轻代和老年代组成。方法区储存类信息,常量,静态变量等数据,是线程共享的区域。栈分为java虚拟机栈和本地方法栈主要用于方法的执行。
HTTP Header
1、通用头 General
Request URL :请求的url
Request Method : 请求的方法,可以是GET、POST
Status Code:HTTP 状态码,表示请求成功
Referrer Policy:当从一个链接跳到另一个链接,另一个链接的referer就记录了是从哪个链接跳来的
2、响应头 Response Headers
Cache-Control:控制HTTP缓存
Content-Encoding:内容的压缩类型,此处是gzip
Content-Length:返回的内容的长度
Content-type:返回的内容类型,此处是html
Connection:keep-alive(TCP连接不会关闭
3、请求头 Request Headers
Accept:浏览器能够接收的内容类型,如text/javascript
Accept-Encoding:浏览器支持的压缩编码类型。
Accept-language:浏览器支持的语言
Referer:发出请求的页面的URL
User-Agent:浏览器的用户代理字符串
JDK1.8hashmap引入了红黑树
当链表长度太长(默认超过8)时,链表就转换为红黑树;
利用红黑树快速增删改查的特点针对超长链的检查,时间复杂度从O(n)降到了O(log2n)
Java线程池的参数,拒绝策略,newFixedThreadPool使用什么阻塞队列
1、corePoolSize:核心线程数
* 核心线程会一直存活,及时没有任务需要执行
* 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
2、queueCapacity:任务队列容量(阻塞队列)
* 当核心线程数达到最大时,新任务会放在队列中排队等待执行
3、maxPoolSize:最大线程数
* 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
* 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
4、 keepAliveTime:线程空闲时间
* 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
5、allowCoreThreadTimeout:允许核心线程超时
6、rejectedExecutionHandler:任务拒绝处理器
* 两种情况会拒绝处理任务:
- 当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务
- 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正
四种策略:
- AbortPolicy 丢弃任务,抛运行时异常
- CallerRunsPolicy 执行任务
- DiscardPolicy 忽视,什么都不会发生
- DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
newFixedThreadPool使用了SynchronousQueue(同步队列)这种队列,这种队列的特点是不缓存数据,而是缓存线程,线程分为生产者线程和消费者线程,一个生产者线程和一个消费者线程是互补的,当一个生产者线程遇到一个消费者线程的时候就会直接进行数据交换。一个线程只能缓存一个数据,当一个线程插入数据之后就会被阻塞,直到另外一个线程消费了其中的数据。
tcp粘包和拆包
原因
1.应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。
2.应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。
3.接收方法不及时读取套接字缓冲区数据,这将发生粘包。
如何解决
使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。
设置定长消息,服务端每次读取既定长度的内容作为一条完整消息。
设置消息边界,服务端从网络流中按消息编辑分离出消息内容。
孤儿进程,僵尸进程和守护进程
1.孤儿进程
如果父进程先退出,子进程还没退出那么子进程将被 托孤给init进程,这是子进程的父进程就是init进程(1号进程)
2.僵尸进程
如果一个进程已经终止了,但是其父进程还没有获取其状态,那么这个进程就称之为僵尸进程.僵尸进程还会消耗一定的系统资源,并且还保留一些概要信息供父进程查询子进程的状态可以提供父进程想要的信息.一旦父进程得到想要的信息,僵尸进程就会结束.
3.守护进程
守护进程就是在后台运行,不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务.习惯上守护进程的名字通常以d结尾(sshd),但这些不是必须的.
Array和ArrayList区别
Array的长度不可变,当数据超过容量大小会报下标越界异常。ArrayList会自动扩容,默认方式是oldCapacity + (oldCapacity >> 1),相当于扩容1.5倍,用位运算性能会好一些
maven项目的打包类型:pom、jar、war
项目中一般使用maven进行模块管理,每个模块下对应都有一个pom文件,pom文件中维护了各模块之间的依赖和继承关系。项目模块化可以将通用的部分抽离出来,方便重用;修改一部分代码不再是build整个项目,缩短了build时间;此外各模块都有自己的pom文件,结构更清晰。
使用maven进行模块划分管理,一般都会有一个父级项目,pom文件除了GAV(groupId, artifactId, version)是必须要配置的,另一个重要的属性就是packaging打包类型,所有的父级项目的packaging都为pom,packaging默认是jar类型,如果不作配置,maven会将该项目打成jar包。作为父级项目,还有一个重要的属性,那就是modules,通过modules标签将项目的所有子项目引用进来,在build父级项目时,会根据子模块的相互依赖关系整理一个build顺序,然后依次build。
而对于各个子项目,需要在其对应的pom文件开头申明对父级项目的引用,通过GAV实现。对于子项目自己的GAV配置,GV如果不配置,则会从父级项目的配置继承过来。子模块可通过dependencies标签来添加自己的依赖,此外子类项目的packaging值只能是war或者jar,前面已经说过,packaging默认是jar类型。如果是需要部署的项目,则需要打包成war类型,如果只是内部调用或者是作服务使用,则推荐打包成jar类型。
线程的几种状态
1.新建状态(New):
当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
2.就绪状态(Runnable)
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
3.运行状态(Running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4.阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
1.线程通过调用sleep方法进入睡眠状态;
2.线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3.线程试图得到一个锁,而该锁正被其他线程持有;
4.线程在等待某个触发条件;
......
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
5.死亡状态(Dead)
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.
找一个数在40亿个数中是否存在
首先知道int的范围[-231,231-1],unsigned int的范围是[0,232-1],都最多有232个数,用Bit-map的方法,申请2^32 bit的空间,即512M[(2^20)x(8)x(512)]的内存,1Byte=8bit,读入要查询的数,查看相应的bit位是否为1,为1表示存在,0表示不存在。如果是要找40亿个数中只出现一次的整数,可以采用2-bitmap(每个数分配2bit,00表示不存在,01表示存在1次,10表示存在多次,11表示无意义),申请1G的内存,然后依次扫描40亿个数,最后输出对应位是01的整数。如果允许一定的出错率可以采用布隆过滤器:原理是采用k个相互独立的hash函数,保证在给定的空间、误判率下,完成元素判重的过程。由于Bitmap不是万能的,如果数据量大到一定程度,就不能用了,而且bitmap只能是对于每一个可能的整型值,通过直接寻址的方式进行映射,相当于一个hash函数。
分布式的常见问题
- 分布式事务:
这是一个老生常谈的问题,我们都知道事务就是一些列操作的原子性保证,在单机的情况下,我们能够依靠本机的数据库连接和组件轻易做到事务的控制,但是分布式情况下,业务原子性操作很可能是跨服务的,这样就导致了分布式事务,例如A和B操作分别是不同服务下的同一个事务操作内的操作,A调用B,A如果可以清楚的知道B是否成功提交从而控制自身的提交还是回滚操作,但是在分布式系统中调用会出现一个新状态就是超时,就是A无法知道B是成功还是失败,这个时候A是提交本地事务还是回滚呢?其实这是一个很难的问题,如果强行保证事务一致性,可以采取分布式锁,但是那样会增加系统复杂度而且会增大系统的开销,而且事务跨越的服务越多,消耗的资源越大,性能越低,所以最好的解决方案就是避免分布式事务。
还有一种解决方案就是重试机制,但是重试如果不是查询接口,必然涉及到数据库的变更,如果第一次调用成功但是没返回成功结果,那调用方第二次调用对调用方来说依然是重试,但是对于被调用方来说是重复调用,例如A向B转账,A-100,B + 100,这样会导致A扣了100,而B增加200。这样的结果不是我们期望的,因此需在要写入的接口做幂等设计。多次调用和单次调用是一样的效果。通常可以设置一个唯一键,在写入的时候查询是否已经存在,避免重复写入。但是幂等设计的一个前提就是服务是高可用,否则无论怎么重试都不能调用返回一个明确的结果调用方会一直等待,虽然可以限制重试的次数,但是这已经进入了异常状态了,甚至到了极端情况还是需要人肉补偿处理。其实根据CAP和BASE理论,不可能在高可用分布式情况下做到一致性,一般都是最终一致性保证。 - 负载均衡
每个服务单独部署,为了达到高可用,每个服务至少是两台机器,因为互联网公司一般使用可靠性不是特别高的普通机器,长期运行宕机概率很高,所以两台机器能够大大降低服务不可用的可能性,这正大型项目会采用十几台甚至上百台来部署一个服务,这不仅是保证服务的高可用,更是提升服务的QPS,但是这样又带来一个问题,一个请求过来到底路由到哪台机器?路由算法很多,有DNS路由,如果session在本机,还会根据用户id或则cookie等信息路由到固定的机器,当然现在应用服务器为了扩展的方便都会设计为无状态的,session会保存到专有的session服务器,所以不会涉及到拿不到session问题。那路由规则是随机获取么?这是一个方法,但是据我所知,实际情况肯定比这个复杂,在一定范围内随机,但是在大的范围也会分为很多个域,例如如果为了保证异地多活的多机房,夸机房调用的开销太大,肯定会优先选择同机房的服务,这个要参考具体的机器分布来考虑。常用算法: 随机算法,轮询,一致性hash - 服务发现
服务的提供者如何被服务的使用者发现,当然很多情况一个服务既是某些服务的提供者,也是其他服务的使用者,这就需要一个中间的机制来让大家互相感知,例如Zookeeper(简称ZK),我简单介绍下ZK的作用和实现,其他类似于spring boot的Eureka和taobao的HSF的配置中心大概都一样的作用,只是不同的技术实现而已。Zookeeper是一个高可用集群组件,因为它可以在集群之间保持同步和一致,维护统一的一个类似于文件目录的结构。ZK包含服务端和客户端,每个机器维护一个客户端保持与服务端的心跳,客户端可以在整个目录的任何节点设置Watcher感知,这样只要ZK维护的该目录有任何改动,客户端都能收到回调通知。如果这个目录存放的某个服务提供者的ip列表,ZK能够感知该服务的心跳,一旦该机器与ZK失去联系,目录变化,服务使用者感知到请求,这样服务使用者调用的时候就获取不到该服务提供者的ip,这样完成动态的失效转移。当服务恢复,ip又添加回原有ZK维护的节点列表。ZK采用了Paxos算法保证了ZK之间的一致性,内部的选举机制保证了集群只要有超过半数的机器正常,集群就可用。高可用的ZK成为一个服务的订阅与通知的中心,这样完成服务发现的功能。 - 数据库性能与高可用
数据库是重要的部分,因为大部门时候我们需要持久化很多数据完成业务逻辑,但是数据库很难像应用服务器那样做到线性的扩容,尤其是关系数据库,所以现在会引入一些对集群支持比较好的NoSQL去支撑系统对性能的要求,但是NoSQL也有很多局限性,例如没有事务支持,这点在一些对事务敏感的情况下是难以忍受的,还有查询的不方便,很难支持join等操作,所以NoSQL的使用对于场景的判断非常重要,现在Redis和Memcache等更多应用在缓存使用上,核心数据库依然采用关系数据库,以MySQL为代表。数据库的性能主要是在查询的优化上,项目实战中DBA经常会警告一些常用的SQL并没走索引进行了全表扫描,所以合理使用索引进行高效的查询是很必要的。当然索引也不是建立的越多越好,例如某些重复率很高的字段不适合建索引,大量的索引甚至比数据本身更占用空间。对于数据量多的情况,我们可以采用分表分库的方案进行拆分,分表一般采用关键字段进行取模,尽量让多个表进行均分。负载过高可以采用主从读写分离等等。
高可用的数据库一般采用主从机制,主库挂了之后会自动转移到备库,曾经跟DBA讨论过,线上不能引入流量到备库,如果性能不够自动扩容,因为如果线上主库挂掉,瞬间流量落到从库依然会挂,到头一台机器也不能用了。原则主从是为了高可用,而不是为了扩容,扩容操作应该在机器负载较高的时候就能收到警报之后。能够失效转移。现在高可用主要的方案也是冗余,包括异地机房等等,本质上都是冗余。 - 脑裂问题
高性能并发服务器的实现
高并发的核心是集群
1、动静分离,静态资源请求与动态请求分离,项目中需要访问的图片、声音、js/css等静态资源需要有独立的存放位置,便于将来实现静态请求分离时直接剥离出来,比如nginx可以直接配置图片文件直接访问目录,而不需要经过tomcat。这样tomcat就可以专注处理动态请求,操作数据库数据处理之类的。静态请求代理服务器性能比tomcat高很多。
2、引入缓存,数据库缓存(一级缓存,二级缓存,分布式缓存)、页面缓存,这东西好用不复杂,搞明白什么地方适用最重要。简单的例子是频繁读取,不修改的地方最适用。也是后续集群做数据共享的一个方式之一,集群环境下,经常会碰到数据共享问题。
3、如果将来数据量大,单一数据库成为瓶颈时,数据库的读写分离来了。数据库集群,读写分离,分表分区。mysql对读写分离这些还是有点支持的,没仔细用过。
4、如果项目发展壮大了,已经过亿用户了,ok系统拆分来了。用户管理系统、订单系统,或者体育新闻系统、娱乐新闻系统等。保证一个系统当掉时不影响另一个,同时分担单系统压力。每个系统之下是前面3条!!
mysql主从复制原理
从库生成两个线程,一个I/O线程,一个SQL线程;
i/o线程去请求主库 的binlog,并将得到的binlog日志写到relay log(中继日志) 文件中;
主库会生成一个 log dump 线程,用来给从库 i/o线程传binlog;
SQL 线程,会读取relay log文件中的日志,并解析成具体操作,来实现主从的操作一致,而最终数据一致;
类型转换
其他类型转string:String s= String.valueOf(value) 其中 value为任意类型
string转int :int i = Integer.parseInt( s );
String转stringbuffer: StringBuffer buffer = new StringBuffer(str);
将字符串变量转换为字符数组:char[] ch=str.toCharArray();
给了一个场景让进行SQL设计,然后优化
Java虚拟机栈里面放的什么东西?
- 局部变量表,顾名思义,想必不用解释大家应该明白它的作用了吧。就是用来存储方法中的局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译器就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。
- 操作数栈,想必学过数据结构中的栈的朋友想必对表达式求值问题不会陌生,栈最典型的一个应用就是用来对表达式求值。想想一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。
- 指向运行时常量池的引用,因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。
- 方法返回地址,当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。
内存泄漏了解多少,想办法写程序把堆内存搞垮,把栈内存呢?方法区呢?
mybatis和hibernate的区别
mybatis算半自动的ORM,着力点在POJO与SQL语句之间的映射,他不会自动生成SQL语句,需程序员自己编写,将SQL参数及返回结果字段映射到指定POJO; hibernate是全自动ORM,实现了POJO和数据库表之间的映射,以及SQL的自动生成和执行。
mybatis中#{}和${}的区别
#{}将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。{}。
分代收集算法,说说你了解的垃圾收集器。具体说说CMS收集器和G1收集器
标记-清除,复制,标记-整理
CMS收集器是一种以获取最短回收停顿时间为目标的收集器。基于“标记-清除”算法实现,它的运作过程如下:
1)初始标记2)并发标记3)重新标记4)并发清除
初始标记仅仅标记GC Root能直接关联到的对象,重新标记是为了修正并发标记期间因用户线程继续运作而导致标记产生变动的那一部分对象的标记记录。这两者会"stop the world",但时间很短。
并发标记主要是GC Root Tracing ,和并发清除这两者耗时较长,但可与用户线程一起工作
主要优点:并发收集、低停顿。
缺点:
1)CMS收集器对CPU资源非常敏感。在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。
2)CMS无法处理浮动垃圾,浮动垃圾是指由于并发清理阶段用户线程还在运行,此时产生的垃圾出现在标记过程之后,CMS无法在当次收集中处理掉他们。
3)CMS是一款“标记--清除”算法实现的收集器,容易出现大量空间碎片。会给大对象的分配造成很大麻烦。
G1与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器(标记整理:整理过程是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存);从局部上来看是基于“复制”算法实现的(复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,先暂停程序的运行,就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次清理掉。缺点:内存缩小为原来的一半)。就不会产生内存空间碎片。
G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集的时间不得超过N毫秒。
多线程的使用场景
1.实现响应更快的应用程序, 即主线程专门监听用户请求,子线程用来处理用户请求。以获得大的吞吐量。比如JavaWeb的就是主线程专门监听用户的HTTP请求,然后启动子线程去处理用户的HTTP请求。
2.某种优先级虽然很低的服务,但是却要不定时去做。比如Jvm的垃圾回收。
volatile关键字,有啥作用,重排序,内存屏障
指令重排序:一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
内存屏障是这样一条cpu指令: a)确保一些特定操作执行的顺序; b)影响一些数据的可见性(可能是某些指令执行后的结果)。
如果你的字段是volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。
这意味着如果你对一个volatile字段进行写操作,你必须知道:
1、一旦你完成写入,任何访问这个字段的线程将会得到最新的值。
2、在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。
线程池的内部参数有哪些,什么含义。如果让你来设计一个线程池你会怎么设计
具体解释一下可重入锁,为啥叫可重入锁
同一个线程再次进入同步代码的时候.可以使用自己已经获取到的锁,这就是可重入锁java里面内置锁(synchronize)和Lock(ReentrantLock)都是可重入的。如果线程A继续再次获得这个锁呢?比如一个方法是synchronized,递归调用自己,那么第一次已经获得了锁,第二次调用的时候还能进入吗? 直观上当然需要能进入.这就要求必须是可重入的.可重入锁又叫做递归锁。
可重入锁的实现
为每个锁关联一个获取计数器和一个所有者线程,当计数值为0的时候,这个所就没有被任何线程只有.当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置为1,如果同一个线程再次获取这个锁,技术值将递增,退出一次同步代码块,计算值递减,当计数值为0时,这个锁就被释放.ReentrantLock里面有实现
SpringMVC与Struts2区别
1、Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC(DispatcherServlet)则采用Servlet实现。
2、拦截机制
1)、Struts2
a、Struts2框架是类级别的拦截,每次请求就会创建一个Action,和Spring整合时Struts2的ActionBean注入作用域是原型模式prototype(否则会出现线程并发问题),然后通过setter,getter吧request数据注入到属性。
b、Struts2中,一个Action对应一个request,response上下文,在接收参数时,可以通过属性接收,这说明属性参数是让多个方法共享的。
c、Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了
2)、SpringMVC
a、SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,所以方法直接基本上是独立的,独享request,response数据。而每个方法同时又何一个url对应,参数的传递是直接注入到方法中的,是方法所独有的。处理结果通过ModeMap返回给框架。
b、在Spring整合时,SpringMVC的Controller Bean默认单例模式Singleton,所以默认对所有的请求,只会创建一个Controller,有应为没有共享的属性,所以是线程安全的,如果要改变默认的作用域,需要添加@Scope注解修改。
Java中公平锁与非公平锁的区别
所谓公平锁指的是哪个线程先运行,那就可以先得到锁。非公平锁是不管线程是否是先运行,都是随机获得锁的。
非公平锁:ReentrantLock()或ReentrantLock(false),synchronized是非公平的
公平锁:ReentrantLock(true)
HashSet保证元素唯一性的原理
使用Set集合是要去除重复元素,如果在存储的时候逐equals()比较,效率太低,哈希算法提高了去重复的效率,减少了使用equals()方法的次数,当HashSet对象调用add()方法存储对象时,会调用对象的HashCode()方法得到一个哈希值,然后在集合中查找是否有哈希值相同的对象,如果用,则调用equals()方法比较,如果没有则直接存入集合。
git和svn的区别
1.git本身关心文件的整体性是否有改变,但多数的 svn系统则在乎文件内容的差异。
- SVN 是集中式或者有中心式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。 SVN 是集中式或者有中心式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。
反射
Java反射框架主要提供以下功能:
1.在运行时判断任意一个对象所属的类;
2.在运行时构造任意一个类的对象;
3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
4.在运行时调用任意一个对象的方法
索引类型?索引失效条件?索引为什么能提高效率?
类型:1.普通索引:基本索引,没什么限制;ADD INDEX index_name ( column
) 2,唯一索引:值必须唯一,但允许有空;ADD UNIQUE (column
) 3.主键索引:一个表只能一个主键;ALTER TABLE table_name
ADD PRIMARY KEY ( column
) 4.全文索引:仅仅用于MyISAM表;5.组合索引:遵循最左前缀原则。ADD INDEX index_name ( column1
, column2
, column3
)
失效条件:1.条件有or(要生效,or条件的每个列都加索引);2.like查询以%开头;3.列类型是字符串,在条件中将数据用引号引用起,否则失效;4.多列字段做索引,要从左到右,否则失效。
索引通过事先排好序,在查找时刻应用二分查找等高效算法,一般的顺序查找时间复杂度为O(n),而二分查找的时间复杂度为O(log2n),当n很大时,二者效率悬殊,例如50w条数据,用二分法不超过20次就能查找到。
session与cookie的区别:
1 隐私策略不同。Cookie存储在客户端,对客户端是可见的,可被客户端窥探、复制、修改。而Session存储在服务器上,不 存在敏感信息泄露的风险
2 Cookie中只能保存ASCII字符串,Session中可以保存任意类型的数据
3 有效期不同。Cookie的过期时间可以被设置很长。只要关闭了浏览器窗口,该Session就会过期。
4 跨域支持不同。Cookie支持跨域访问(设置domain属性实现跨子域),Session不支持跨域访问
SQL语句优化和索引优化
SQL语句优化
1.like语句优化:由于abc前面用了“%”,因此该查询必然走全表查询,除非必要,否则不要在关键词前加%
2.where后面的条件顺序: 连接条件放前面, 而其他的条件放后面,由于sql从右侧往左侧执行,此时可以过滤掉大部分数据
3.where子句使用 != 或 <> 操作符优化
在where子句中使用 != 或 <>操作符,索引将被放弃使用,会进行全表查询
如SQL:SELECT id FROM A WHERE ID != 5 优化成:SELECT id FROM A WHERE ID>5 OR ID<5
4.where子句中索引列使用 IS NULL 或 IS NOT NULL 的优化
在where子句中索引列使用 IS NULL 或 IS NOT NULL 判断,索引将被放弃使用,会进行全表查询。非索引列不影响
如SQL:SELECT id FROM A WHERE num IS NULL 优化成num上设置默认值0,确保表中num没有null值,然后SQL为:SELECT id FROM A WHERE num=0
5.where子句使用or的优化
很多时候使用union all 或 union(必要的时候)的方式替换“or”会得到更好的效果。where子句中使用了or,索引将被放弃使用。
如SQL:SELECT id FROM A WHERE num =10 or num = 20 优化成:SELECT id FROM A WHERE num = 10 union all SELECT id FROM A WHERE num=20
6.任何地方都不要用 select * from table ,用具体的字段列表替换"*",不要返回用不到的字段
7.where字句里不要用函数判断
索引优化
1、表的主键、外键必须有索引;
2、数据量超过300的表应该有索引;
3、经常与其他表进行连接的表,在连接字段上应该建立索引;
4、索引应该建在选择性高的字段上;
5、频繁进行数据操作的表,不要建立太多的索引;
6、删除无用的索引,避免对执行计划造成负面影响;
如何设计一个高并发的系统
① 数据库的优化,包括合理的事务隔离级别、SQL语句优化、索引的优化
② 使用缓存,尽量减少数据库 IO
③ 分布式数据库、分布式缓存
④ 服务器的负载均衡
分治法与动态规划
分治法所能解决的问题的一般有以下的特征:
(1)该问题的规模缩小到一定的程度就可以容易解决(绝大多数的问题都满足)
(2)该问题是可以分解为若干个规模较小的相同的问题,即改问题具有最优子结构性质(前提,也是绝大多数的问题都满足的)
(3)利用该问题分解出的字问题的解可以合并该问题(关键)
(4)该问题分解出来的各个子问题是相互独立的(分治法的效率)
(如果具备一二条,但不具备第三条,可以考虑使用贪心算法或动态规划)
动态规划
1.简单的说就是在解决多阶决策的过程中动态的选择最优的过程的方法就是动态规划。
2.与分治法的区别:一般来说子问题不是互相独立的,可以理解为是分治法的一种改进,不需要求解已有解的子问题(已有解的子问题用表来记录)
3.适用条件:
(1)最优化原理(最优子结构):一个最优策略的子策略总是最优的---->局部最优,整体最优
(2)无后效性:每个状态都是过去历史的一个完整的总结
(3)子问题的重叠性:高效性
例子:子序列最大和问题,滑雪问题
贪心算法
条件:每一步的最优解一定依赖上一步的最优解。
方法:从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止。
存在问题:
(1)不能保证求得的最后解是最佳的;
(2)不能用来求最大最小解的问题;比如钱币分为1元3元4元,要拿6元钱,贪心的话,先拿4,再拿两个1,一共3张钱;实际最优却是两张3元就够了。
“贪心”特性:它对解空间树的遍历不需要自底向上,而只需要自根开始,选择最优的路,一直走到底就可以了。这样,与动态规划相比,它的代价只取决于子问题的数目,而选择数目总为1。
例子:哈夫曼树,钱币组合,设置雷达问题
关系数据库的第一第二第三范式?
1NF是所有关系型数据库的最基本要求,满足这个要求才能创建数据表;
2NF:消除了非主属性对于码的部分函数依赖;
假如当 K 确定的情况下,该表除 K 之外的所有属性的值也就随之确定,那么 K 就是码
3NF:消除了非主属性对于码的传递函数依赖。
设计模式
工厂方法模式:spring中创建实例时配置Factorybean(动态工厂bean)
抽象工厂模式:工厂方法模式中每个工厂类只能创建一个具体产品类的实例,抽象工厂模式每个具体工厂类可以创建多个具体产品类的实例
单例模式:spring中的bean默认是单例的
适配器模式:AOP中advisor链需要MethodIntercepter对象,所以每一个advisor中的advice都要适配成对应的methodInterceptor对象
装饰模式:spring中类名有wrapper,decorator,动态给对象添加额外的职责
注意:在装饰器模式中,装饰者和被装饰者必须有共同的父类;一般情况下,被装饰者中有装饰者(或者他们共同的父类)的引用,通过构造函数来实现
代理模式:AOP中JDK动态代理和CGlib代理
观察者模式:spring中常用地方是listener的实现
策略模式:spring的事务管理策略,只提供事务管理接口
JDK源码中的设计模式
装饰者模式:动态的给一个对象附加额外的功能,这也是子类的一种替代方式,IO中。
代理模式:远程方法调用RMI
迭代器模式:Iterator
观察者模式:Listener,它使得一个对象可以灵活的将消息发送给感兴趣的对象
快排思想
选定一个切分元素,将比这个数大的数放到右边,小于或等于它的数放到左边,再对左右区间递归,采用分治策略。
DNS解析过程
1、在浏览器中输入www.qq.com域名,操作系统会先检查自己浏览器DNS缓存,然后是本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。
2、如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。
3、如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/ip参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。
4、如果要查询的域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性。
5、如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址(qq.com)给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找qq.com域服务器,重复上面的动作,进行查询,直至找到www.qq.com主机。
6、如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。不管是本地DNS服务器用是是转发,还是根提示,最后都是把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机。
从客户端到本地DNS服务器是属于递归查询,而DNS服务器之间的交互查询就是迭代查询。
TCP/UDP的区别?TCP协议如何保证可靠传输?
区别
1.基于连接与无连接
2.TCP要求系统资源较多,UDP较少;
3.UDP程序结构较简单
4.流模式(TCP)与数据报模式(UDP);
5.TCP保证数据正确性,UDP可能丢包
6.TCP保证数据顺序,UDP不保证
TCP协议如何保证可靠传输:
1、应用数据被分割成TCP认为最适合发送的数据块。
2、超时重传:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
3、TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
4、校验和:TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。
5、TCP的接收端会丢弃重复的数据。
6、流量控制:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的我数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。
7、拥塞控制:当网络拥塞时,减少数据的发送。
1.静态代码块和非静态代码块的区别
静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)静态代码块只在第一次new执行一次,之后不再执行,而非静态代码块在每new一次就执行一次。非静态代码块可在普通方法中定义(不过作用不大);而静态代码块不行。
2.hashmap进行扩容,底层结构
hashmap底层结构是数组+链表,Entry 实际上就是一个单向链表。采用链地址法解决hash冲突,没有冲突的理想情况下增加和删除的时间复杂度都是O(1)
那么hashmap什么时候进行扩容呢?当hashmap中的元素个数超过数组大小乘以loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16乘以0.75=12的时候,就把数组的大小扩展为2乘以16=32,即扩大一倍,这里就是使用一个容量更大的数组来代替已有的容量小的数组,transfer()方法将原有Entry数组的元素拷贝到新的Entry数组里。然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.75乘以1000 < 1000, 也就是说为了让0.75 乘以 size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。
3.内存泄漏
内存泄露的定义:对于应用程序来说,当对象已经不再被使用,但是Java的垃圾回收器不能回收它们的时候,就产生了内存泄露。
为什么内存泄露会发生::
让我们用下面的例子来看看为什么会发生内存泄露。如下图所示,对象A引用对象B,A的生命周期(t1-t4)比B的生命周期(t2-t3)要长,当B在程序中不再被使用的时候,A仍然引用着B。在这种情况下,垃圾回收器是不会回收B对象的,这就可能造成了内存不足问题,因为A可能不止引用着B对象,还可能引用其它生命周期比A短的对象,这就造成了大量无用对象不能被回收,且占据了昂贵的内存资源。
同样的,B对象也可能引用着一大堆对象,这些被B对象引用着的对象也不能被垃圾回收器回收,所有的这些无用对象消耗了大量内存资源。
怎样阻止内存泄露
1.使用List、Map等集合时,在使用完成后赋值为null
2.使用大对象时,在用完后赋值为null
3.目前已知的jdk1.6的substring()方法会导致内存泄露
4.避免一些死循环等重复创建或对集合添加元素,撑爆内存
5.简洁数据结构、少用静态集合等
6.及时的关闭打开的文件,socket句柄等
7.多关注事件监听(listeners)和回调(callbacks),比如注册了一个listener,当它不再被使用的时候,忘了注销该listener,可能就会产生内存泄露
4.乐观锁和悲观锁
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。[1]
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。[1] 乐观锁不能解决脏读的问题。
乐观锁
大多使用数据版本(Version)记录机制实现,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据
悲观锁
需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。
使用:在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.
5.数据库索引(b tree,b+tree)
B树即多路平衡查找树,一个 m 阶的B树满足以下条件:
- 每个结点至多拥有m棵子树;
- 根结点至少拥有两颗子树(存在子树的情况下);
- 除了根结点以外,其余每个分支结点至少拥有 m/2 棵子树;
- 所有的叶结点都在同一层上;
- 有 k 棵子树的分支结点则存在 k-1 个关键字,关键字按照递增次序进行排列;
B+树的特性:
1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
2.不可能在非叶子结点命中;
3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
4.更适合文件索引系统;
6.Maven生命周期
Maven有三套相互独立的生命周期,请注意这里说的是“三套”,而且“相互独立”,初学者容易将Maven的生命周期看成一个整体,其实不然。这三套生命周期分别是:
Clean Lifecycle 在进行真正的构建之前进行一些清理工作。
Default Lifecycle 构建的核心部分,编译,测试,打包,部署等等。
Site Lifecycle 生成项目报告,站点,发布站点。
我再次强调一下它们是相互独立的,你可以仅仅调用clean来清理工作目录,仅仅调用site来生成站点。当然你也可以直接运行 mvn clean install site 运行所有这三套生命周期。
7、HashMap与HashTable有什么区别?对比Hashtable VS HashMap
两者都是用key-value方式获取数据。Hashtable是原始集合类之一(也称作遗留类)。HashMap作为新集合框架的一部分在Java2的1.2版本中加入。它们之间有一下区别:
- HashMap和Hashtable大致是等同的,除了非同步和空值(HashMap允许null值作为key和value,而Hashtable不可以)。
- HashMap没法保证映射的顺序一直不变,但是作为HashMap的子类LinkedHashMap,如果想要预知的顺序迭代(默认按照插入顺序),你可以很轻易的置换为HashMap,如果使用Hashtable就没那么容易了。
- HashMap不是同步的,而Hashtable是同步的。
- 迭代HashMap采用快速失败机制,而Hashtable不是,所以这是设计的考虑点。
java的快速失败和安全失败
快速失败:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改
安全失败:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。
8、在Hashtable上下文中同步是什么意思?
同步意味着在一个时间点只能有一个线程可以修改哈希表,任何线程在执行hashtable的更新操作前需要获取对象锁,其他线程等待锁的释放。
9、怎样使Hashmap同步?
HashMap可以通过Map m = Collections.synchronizedMap(hashMap)来达到同步的效果。
10、什么时候使用Hashtable,什么时候使用HashMap
基本的不同点是Hashtable同步HashMap不是的,所以无论什么时候有多个线程访问相同实例的可能时,就应该使用Hashtable,反之使用HashMap。非线程安全的数据结构能带来更好的性能。
如果在将来有一种可能—你需要按顺序获得键值对的方案时,HashMap是一个很好的选择,因为有HashMap的一个子类 LinkedHashMap。所以如果你想可预测的按顺序迭代(默认按插入的顺序),你可以很方便用LinkedHashMap替换HashMap。反观要是使用的Hashtable就没那么简单了。同时如果有多个线程访问HashMap,Collections.synchronizedMap()可以代替,总的来说HashMap更灵活。
11、为什么Vector类认为是废弃的或者是非官方地不推荐使用?或者说为什么我们应该一直使用ArrayList而不是Vector
你应该使用ArrayList而不是Vector是因为默认情况下你是非同步访问的,Vector同步了每个方法,你几乎从不要那样做,通常有想要同步的是整个操作序列。同步单个的操作也不安全(如果你迭代一个Vector,你还是要加锁,以避免其它线程在同一时刻改变集合).而且效率更慢。当然同样有锁的开销即使你不需要,这是个很糟糕的方法在默认情况下同步访问。你可以一直使用Collections.sychronizedList来装饰一个集合。
事实上Vector结合了“可变数组”的集合和同步每个操作的实现。这是另外一个设计上的缺陷。Vector还有些遗留的方法在枚举和元素获取的方法,这些方法不同于List接口,如果这些方法在代码中程序员更趋向于想用它。尽管枚举速度更快,但是他们不能检查如果集合在迭代的时候修改了,这样将导致问题。尽管以上诸多原因,oracle也从没宣称过要废弃Vector。
12.Concurrenthashmap的细节
Hashtable中采用的锁机制synchronizedmap是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而Concurrenthashmap中则是一次锁住一个桶,Concurrenthashmap默认将hash表分为16个桶,诸如get,put,remove等操作值锁当前需要用到的桶,原来只有一个线程进入,现在却能同时有16个写线程执行,并发性能大大提升。ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素, 每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁
13.进程和线程
进程是资源分配的基本单位,表示一个程序的运行活动,线程是调度的基本单位,是进程中不同执行路径。进程包含线程,线程共用进程资源。
14.Java内存模型是什么?
Java内存模型规定了所有的变量都存储在主内存(Main Memory)中。每条线程还有自己的工作内存( Working Memory)。线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能自接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。包含了:
1.线程内的代码能够按先后顺序执行,这被称为程序次序规则。
2.对于同一个锁,一个解锁操作一定要发生在时间上后发生的另一个锁定操作之前,也叫做管程锁定规则。
3.前一个对volatile的写操作在后一个volatile的读操作之前,也叫volatile变量规则。
4.一个线程内的任何操作必需在这个线程的start()调用之后,也叫作线程启动规则。
5.一个线程的所有操作都会在线程终止之前,线程终止规则。
6.一个对象的终结操作必需在这个对象构造完成之后,也叫对象终结规则。
7.如果操作A先行发生于操作B,操作B先行发生于操作C,就可以得出操作A先行发生于操作C。
8.线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
15.Thread 类中的start() 和 run() 方法有什么区别?
这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。
16.Java中的volatile 变量是什么?
volatile是一个特殊的修饰符,只有成员变量才能使用它,volatile关键字解决的是内存可见性的问题,会使得所有对volatile变量的读写都会直接刷到主存,即保证了变量的可见性。volatile变量可以保证下一个读取操作会在前一个写操作之后发生,就是上一题的volatile变量规则。
17 Java多线程中调用wait() 和 sleep()方法有什么不同?
答:sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
18.ACID
- 原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
- 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
19.解决hash冲突的方法
链地址法、开放地址法(伪随机探测法)、再散列法、建立一个公共溢出区。
20.TCP的滑动窗口机制
TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。每个TCP/IP主机支持全双工数据传输,因此TCP有两个滑动窗口:一个用于接收数据,另一个用于发送数据。TCP使用肯定确认技术,其确认号指的是下一个所期待的字节。 假定发送方设备以每一次三个数据包的方式发送数据,也就是说,窗口大小为3。发送方发送序列号为1、2、3的三个数据包,接收方设备成功接收数据包,用序列号4确认。发送方设备收到确认,继续以窗口大小3发送数据。当接收方设备要求降低或者增大网络流量时,可以对窗口大小进行减小或者增加,本例降低窗口大小为2,每一次发送两个数据包。当接收方设备要求窗口大小为0,表明接收方已经接收了全部数据,或者接收方应用程序没有时间读取数据,要求暂停发送。发送方接收到携带窗口号为0的确认,停止这一方向的数据传输。
21.线程池
线程池:基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的资源开销,提高了系统的性能。
String 转出 int型,判断能不能转?如何转?
如果只是判断是否都是数字,可以用正则表达式匹配:s.matches("-?\d+")。 如果要判断是否在int范围内,用try catch。用Integer.parseInt(s)将字符串转为Int
讲讲多线程?多线程与多进程的区别
什么是多线程:1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务
多线程的优点:能适当提高程序的执行效率,能适当提高资源利用率(CPU、内存利用率)
多线程的缺点:开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能;线程越多,CPU在调度线程上的开销就越大;程序设计更加复杂:比如线程之间的通信、多线程的数据共享
两者都可以提高程序的并发度,提高程序运行效率和响应时间。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:
1。速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。
2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。
3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。
你用过的一些collection都有哪些
collection是个接口(常用作集合用),它下面有两个子接口分别为:(1)List(2)set
其中List是有序可重复集,set是无序不可重复集。
List又分为三类(1)ArrayList(2)LinkList(3)Vector
ArrayList内部由数组实现,适合查询;LinkList内部由链表实现,适合增删改。Vector几乎用不到
ArrayList 对于随机位置的add/remove,时间复杂度为 O(n),但是对于列表末尾的添加/删除操作,时间复杂度是 O(1). 对于get/set,时间复杂度是O(1)
LinkedList对于随机位置的add/remove,时间复杂度为 O(n),但是对于列表 末尾/开头 的添加/删除操作,时间复杂度是 O(1).
对于get/set,时间复杂度为O(n)
set又分为(1)HashSet(2)treeSet treeSet是二叉树,有序的 ;HashSet采用散列存储,是无序的。另外collection是集合的接口,collections是集合的工具类
treemap的实现?
红黑树。
equls 和 == 的区别
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
java对象序列化
把java对象转换为字节序列,通过实现Serializable接口,比如将Httpsession对象保存到文件系统或数据库等
java线程间通信方式:
1.synchronized同步2.while轮询3.wait/notify4.管道通信
堆内存的分代回收
Java针对堆的垃圾回收,将堆分为了三个较小的部分:新生代、老年代。新生代主要使用复制和标记-清除垃圾回收算法,年老代主要使用标记-整理垃圾回收算法,因此java虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器。
重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。
抽象类(abstract class)和接口(interface)有什么异同?
答:抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。区别:接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法。使用方式上抽象类只有通过继承被使用,接口只能通过实现被使用。
Java 中的final关键字有哪些用法?
答:(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;
(3)修饰方法参数:不能改变参数的值
public void finalFunc(final int i, final Value value)
(4)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
数据类型之间的转换:
如何将字符串转换为基本数据类型?- 调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型;
如何将基本数据类型转换为字符串?- 一种方法是将基本数据类型与空字符串(”")连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf()方法返回相应字符串
阐述final、finally、finalize的区别。
final:修饰符(关键字)有三种用法:(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
finally:通常放在try…catch…的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
finalize:Object类中定义的方法, finalize()方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。
Collection和Collections的区别?
答:Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
代理模式
静态代理:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2.缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
动态代理是指程序在整个运行过程中根本不存在目标的代理类,代理对象是由代理生成工具(如代理工厂)在程序运行时由JVM根据反射等机制动态生成的,代理对象与目标对象的代理关系在程序运行时才确立。
JDK的动态代理:
1.代理对象,不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
2.代理对象的生成,是利用JDK的API(newProxyInstance),动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
CGLIB代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。所以,使用CGLIB生成动态代理,要求目标类必须能够被继承,即不能是final的类。Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
Lock和synchronized的区别?
如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况: 1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;2)线程执行发生异常,此时JVM会让线程自动释放锁。那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,就会影响程序执行效率,因此就需要有一种机制可以不让等待的线程一直无期限地等待下去,通过Lock就可以办到。还有一种是通过Lock使得多个线程都只是进行读操作时,线程之间不会发生冲突。并且通过Lock可以知道线程有没有成功获取到锁。
从输入url到页面加载完成都发生了什么事情?
根据域名查找IP地址
通过三次握手与目标服务器建立TCP连接
主机通过TCP连接向服务器发起HTTP请求
服务器响应请求,返回HTTP报文
浏览器接收响应,根据HTTP状态码做出进一步的处理
浏览器将页面渲染到视窗中
HTTP状态码
HTTP,HTTPS
http的不足:
1.通信使用明文,可能被窃听
2.不验证通信方身份,可能遭遇伪装
3.无法证明报文完整性,可能已遭篡改
Http+加密+认证+完整性保护=https
http优点:
1.不保存用户状态
2.持久连接
3.管线化(可同时发多个请求)
http1.0和http1.1的区别,http2.0
1.长连接和短连接
长连接指一个TCP连接可以发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方法检测包以维持此链接。短链接是指双方由数据交互时,就建立一个连接,数据发送完成后就断开连接。
2.http1.1提供了身份认证,状态管理和cache缓存相关的请求头与响应头
http/2
HTTP/2
1.在应用层(HTTP/2)和传输层之间增加一个二进制分帧层,实现了多路复用来改进传输性能,实现低延迟和高吞吐量。
2.首部压缩,利用静态字典和动态字典存键值对,传输使用字符代替,并且只发送差异数据,大大提高了传输性能。
3.http/2还支持服务端推送,服务端推送是一种在客户端请求之前发送数据的机制。
请求报文与响应报文
请求报文:请求方法+请求URL+协议版本+可选请求首部字段+内容实体
响应报文:协议版本+状态码+原因短语+可选的响应首部字段+实体主体
AOP的具体实现,配置bean?
定义业务接口与实现类IUserService和UserServiceImpl
定义切面POJO类:MyAspect,其中的方法将作为不同的通知方法
在POJO类上添加@Aspect注解,在类的方法上添加不同的通知注解
注册目标对象与POJO类,以及AspectJ的自动代理
- 测试类中使用目标对象的id
PUT和POST?
PUT用来新增和完整更新,必须包含item所有属性资料。POST用来新增,可以只更新item一条属性。
Socket编程客户端的实现?
建立与服务器端口的Tcp连接
想服务器端口发送请求
接收数据
关闭Socket
Redis?redis和memcached的区别?
Redis是一个Key-Value类型的内存数据库
(1) 因为数据存在内存中,所以速度快,性能出色。
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
(5) redis可以持久化其数据
redis和memcached的区别?
1.redis支持更丰富的数据类型
2.redis支持数据的备份,即master-slave模式的数据备份
3.redis支持持久化
关系型数据库和非关系型数据库?
非关系型数据库的优势:1. 性能NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高。2. 可扩展性同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。关系型数据库的优势:1. 复杂查询可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。2. 事务支持使得对于安全性能很高的数据访问要求得以实现
数据库索引的作用?
答:创建索引可以大大提高系统的性能。
1. 通过创建唯一性索引可以保证表中每行数据的唯一性
2. 大大加快数据库检索速度
3. 加速表和表之间的连接
4. 在使用分组(group by)和排序(order by)子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
JVM的内存区域划分?
运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。http://www.importnew.com/18961.html
Java虚拟机栈
- 局部变量表,顾名思义,想必不用解释大家应该明白它的作用了吧。就是用来存储方法中的局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译器就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。
- 操作数栈,想必学过数据结构中的栈的朋友想必对表达式求值问题不会陌生,栈最典型的一个应用就是用来对表达式求值。想想一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。
- 指向运行时常量池的引用,因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。
- 方法返回地址,当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。
本地方法栈
本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。
方法区
方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。
在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。
在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。
堆
Java中的堆是用来存储对象本身的以及数组
JVM调优
1.依赖应用程序对象生命周期分布设置年轻代和老年代,如果存在大量临时对象,选择更大的年轻代,如果存在较多的持久对象,老年代适当增大
2.为老年代选择并行收集算法
bean除了单例还有什么实现?
另一种和singleton对应的scope值---prototype多实例模式,调用getBean时,就new一个新实例
Linux常用命令?
Cd:进入目录
Ls :查看目录中的文件
Mkdir : 创建目录
Rm : 删除文件或目录
Cp : 复制一个文件
Cat more less :显示文件内容,more提供分页,less提供翻页,跳转,查找
Pwd :显示当前路径
Top :显示进程资源占用情况
Cmp :查看两个文件内容是否一致
画红黑树?
红黑树性质:1.每个节点是红色或黑色2.根节点是黑色3.叶节点(NIL)是黑色4.每个红色节点的两个子节点都是黑色5.每个节点到其所有后代叶节点的简单路径上,均包含数目相同的黑色节点。
原理:http://blog.csdn.net/u014634338/article/details/78007490
实现:http://blog.csdn.net/liuweiyuxiang/article/details/78828313
各种排序的应用场景?
- 稳定性比较
插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的。
选择排序、希尔排序、快速排序、堆排序是不稳定的。- 时间复杂性比较
插入排序、冒泡排序最优为O(n),最坏为O(n^2), 平均O(n^2);
快速排序最优为O(nlogn),最坏为O(n^2),平均O(nlogn);
堆排序最优为O(nlogn),最坏为O(nlogn),平均O(nlogn);线形排序的时间复杂性为O(n)。- 辅助空间的比较
线形排序、归并排序的辅助空间为O(n),快速排序的辅助空间为O(logn),其它排序的辅助空间为O(1)。- 其它比较
插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。
反而在这种情况下,快速排序慢了。 当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。- 若待排序的记录的关键字在一个明显有限范围内时,且空间允许时用桶排序。
- 当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。
- 当n较大时,关键字元素可能出现本身是有序的,对稳定性有要求时,空间允许的情况下宜用归并排序。
- 当n较大时,关键字元素可能出现本身是有序的,对稳定性没有要求时宜用堆排序。
Spring mvc的DispatcherServlet工作机制?
1.浏览器将请求发送给中央处理器
2.中央处理器将请求转给处理器映射器处理
3.处理器映射器会根据请求找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器
4.中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器
5.处理器适配器调用执行处理器
6.处理器将执行结果及要跳转的视图封装到ModelAndView中,并将其返回给处理器适配器
7.处理器适配器直接将结果返回给中央调度器
8.中央调度器调用视图解析器,将ModelAndView中的视图名称封装为视图对象
9.视图解析器将试图对象返回给中央调度器
10.中央调度器调用视图对象,让其自己进行渲染,形成相应对象
11.中央调度器响应浏览器
数据库事务,事务隔离级别
事务是指用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。
隔离级别:read uncommitted、read committed、repeatable read(可重读)、serializable(串行化)
我们将事务隔离级别设置为read uncommitted,即便是事务没有commit,但是我们仍然能读到未提交的数据,这是所有隔离级别中最低的一种。我们叫脏读
当我们将当前会话的隔离级别设置为read committed的时候,当前会话只能读取到其他事务提交的数据,未提交的数据读不到。那就是我们在会话B同一个事务中,读取到两次不同的结果。这就造成了不可重复读。
当我们将当前会话的隔离级别设置为repeatable read(---MySQL默认的隔离级别)的时候,当前会话可以重复读,就是每次读取的结果集都相同,而不管其他事务有没有提交。数据已经发生改变,但是我还是要保持一致。这种现象叫幻读。
当我们将当前会话的隔离级别设置为serializable的时候,其他会话对该表的写操作将被挂起。可以看到,这是隔离级别中最严格的,但是这样做势必对性能造成影响。
(//www.greatytc.com/p/4e3edbedb9a8)
git常用命令
git init 本地初始化仓库git clone 获取远程仓库内容git status 查询仓库状态git log 显示分支的提交记录git add 会递归地添加当前工作目录中的所有文件git commit 提交已经被add进来的改动
https://www.cnblogs.com/my--sunshine/p/7093412.html
对象是否存活
1.引用计数法
2.可达性分析、
java中可作为GCroot的对象有:
1.VM栈中引用的对象
2.本地方法栈中引用的对象
3.方法区中常量引用的对象
4.方法区中静态属性引用的对象
IoC 和 DI的区别?IoC的好处?
IoC 控制反转,指将对象的创建权,反转到Spring容器 , DI 依赖注入,指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
BeanFactory 接口和 ApplicationContext 接口有什么区别 ?
ApplicationContext 接口继承BeanFactory接口,Spring核心工厂是BeanFactory ,BeanFactory采取延迟加载,第一次getBean时才会初始化Bean, ApplicationContext是会在加载配置文件时初始化Bean。
ApplicationContex接口对BeanFactory(是一个子接口)进行了扩展,在BeanFactory的基础上添加了其他功能,比如与Spring的AOP更容易集成,也提供支持国际化的文本消息、事件传播以及应用层的特别配置,比如针对Web应用的WebApplicationContext。
IoC的好处在于,最大限度地降低对象之间的耦合度,把对象的创建、和对象的依赖关系控制权都交给了spring,这样,要发生更改时,只需修改一下配置就好了
bean的配置实例化有哪些方式?
1) 无参构造器实例化(默认无参数)
<bean id="bean1" class="cn.itcast.spring.b_instance.Bean1"></bean>
2) 静态工厂方法实例化(简单工厂模式)
<bean id="bean2" class="cn.itcast.spring.b_instance.Bean2Factory" factory-method="getBean2"></bean>
3) 动态工厂方法实例化(工厂方法模式)
<beanid="bean3Factory"class="cn.itcast.spring.b_instance.Bean3Factory"></bean>
<bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>
Spring框架中Bean的生命周期和作用域
1.bean初始化
有两种方式初始化:
A.在配置文件中通过指定init-method属性来完成
B.实现InitializingBean接口
2.bean调用
有三种方式可以得到bean实例,并进行调用
3.bean销毁
销毁有两种方式
A.使用配置文件指定的destroy-method属性
B.实现DisposeableBean接口
作用域
singleton
当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
prototype
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean() 方法)时都会创建一个新的bean实例。根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用 singleton作用域
Request ,Session , global Session :都是一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
spring支持Bean的注入方式?
支持构造器注入和setter方法注入
构造器注入,通过 <constructor-arg> 元素完成注入
setter方法注入, 通过<property> 元素完成注入【开发中常用方式】
什么是AOP?
面向切面编程,在面向切面编程的思想里,把功能分为核心业务功能,和周边功能。所谓核心业务,比如登陆,增删数据,所谓周边功能,比如日志,事务管理。周边功能在Spring的AOP思想里,即被定义为切面,在AOP的思想里,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能编织在一起,这就叫AOP.
什么时候可以用到java消息机制?
答:(1)异构系统集成,整合现有资源,提高资源的利用率
(2)异步请求处理,减轻或消除系统瓶颈,提高用户生产率和系统的整体可伸缩性
(3)组件解偶,增加系统的灵活性
同一个controller能否返回不同格式的数据
Mysql, Mybatis批量操作
以执行f:\test\目录下所有的SQL脚本为例, 其批处理代码如下:
@echo off
for %%i in (f:\test*.sql) do (
echo excute %%i
mysql -uroot -p123456 < %%i
)
echo success
pause
若是当前目录下, 可将”f:\test.sql” 改为”..sql” 即可
Mybatis的批处理有Union all先联结 或者foreach批量插入
GC
对象在Eden出生,并经过第一次minorGC后仍然存活,并能被survivor容纳的话,将被移动到survivor空间中,并且对象年龄设为1,对象在Survivor中没“熬过”一次minorGC,年龄就增加1岁,当增加到一定程度(默认15岁),就会被晋升到老年代,老年代会有majorGC,未被引用的对象会被回收。大对象直接进入老年代。
java类加载
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构
类加载的过程包括了加载、验证、准备、解析、初始化五个阶段
加载时类加载过程的第一个阶段,在加载阶段,虚拟机需要完成以下三件事情:
1、通过一个类的全限定名来获取其定义的二进制字节流。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
验证:确保被加载的类的正确性
准备:为类的静态变量分配内存,并将其初始化为默认值
解析:把类中的符号引用转换为直接引用
初始化:为类的静态变量赋予正确的初始值
双亲委派机制:双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。意义是防止内存中出现多份同样的字节码。
死锁,解决死锁?
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象
死锁的发生必须具备以下四个必要条件
互斥条件(Mutual exclusion) :资源不能被共享,只能由一个进程使用。
请求与保持条件(Hold and wait):进程已获得了一些资源,但因请求其它资源被阻塞时,对已获得的资源保持不放。
不可抢占条件(No pre-emption) :有些系统资源是不可抢占的,当某个进程已获得这种资源后,系统不能强行收回,只能由进
程使用完时自己释放。
循环等待条件(Circular wait) :若干个进程形成环形链,每个都占用对方申请的下一个资源
进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
同步可解决死锁:Synchronized,Lock显示锁,信号量控制
对待死锁的策略主要有:
(1) 死锁预防:破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。
(2) 死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。
(3) 死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。
(4) 死锁解除:这是与死锁检测结合使用的,它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。
中间件、消息队列
中间件是在系统之上,应用软件之下,为处于自己上层的应用软件提供运行与开发环境的各种组件,中间件屏蔽了底层操作系统的复杂性,是开发人员的开发环境变得简单。
分类:1.数据访问中间件2.远程调用中间件3.消息队列中间件4.对象中间件 Tomcat Jboss 都属于中间件
消息队列中间件主要解决
应用耦合: 订单——库存系统、
异步消息: 注册时发送短信和邮件、以及处理后序根据用户信息的推荐工作等等
流量削峰:秒杀系统、
消息通讯:聊天室
https请求过程
MySql两种存储引擎的区别
MyISAM:
- 不支持事务,不支持外键,支持全文索引;
- 支持表级锁,即每次操作是对整个表加锁;
- 存储表的总行数;
- 一个MYISAM表有三个文件:索引文件、表结构文件、数据文件;
- 采用非聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性。
MyISAM引擎使用的生产业务场景:不需要事务支持的业务,以读为主的业务,对数据一致性要求不是很高的业务。
InnoDb: - 支持ACID的事务,支持事务的四种隔离级别;
- 支持行级锁及外键约束:因此可以支持写并发;
- 不存储总行数;
4.一个InnoDb引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为2G),受操作系统文件大小的限制;
5.主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。
集合类
Collection(list,set)和map
arraylist如何快速排序
一种是使用 Comparable 另一种是使用 Comparator,Comparable 对象可以说“我可以自己与另外一个对象比较”而一个 Comparator 对象可以说“我可以比较两个不同的对象”
NIO与BIO的区别,为何NIO能提高效率,NIO三大核心,AIO
NIO面向buffer避免了资源浪费
BIO:同步阻塞式IO,客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,如果BIO要能够同时处理多个客户端请求,就必须使用多线程。适用于连接数目比较小,比如下载
NIO:同步非阻塞IO,关键是采用了事件驱动的思想来实现了一个多路转换器,即客户端发送的连接请求都会注册到多路复用器上,这样只需要开启一个线程就可以处理来自多个客户端的IO事件。NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java nio实现。采用Reactor模式
AIO:异步非阻塞IO,另外开线程来通信,通信完成会调用类似的回调函数来通知,采用Proactor模式。AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器。
Reactor被动的等待指示事件的到来并做出反应;它有一个等待的过程,做什么都要先放入到监听事件集合中等待handler可用时再进行操作;
Proactor直接调用异步读写操作,调用完后立刻返回。
为什么NIO能提高效率
1.io是面向流的,也就是读取数据的时候是从流上逐个读取,所以数据不能进行整体以为,没有缓冲区;nio是面向缓冲区的,数据是存储在缓冲区中,读取数据是在缓冲区中进行,所以进行数据的偏移操作更加方便
2,io是阻塞的,当一个线程操作io时如果当前没有数据可读,那么线程阻塞,nio由于是对通道操作io,所以是非阻塞,当一个通道无数据可读,可切换通道处理其他io
3,nio有selecter选择器,就是线程通过选择器可以选择多个通道,而io只能处理一个
NIO三大核心
1.buffer:NIO直接读到buffer中操作
2.channel:可以以非阻塞状态运行
3.selector:分发请求到不同的channel
NIO是同步非阻塞,AIO是异步非阻塞,AIO在读写完成之后才被通知,能够胜任读写过程长的应用。
Dubbo
上图中,蓝色的表示与业务有交互,绿色的表示只对Dubbo内部交互。上述图所描述的调用流程如下:
服务提供方发布服务到服务注册中心;
服务消费方从服务注册中心订阅服务;
服务消费方调用已经注册的可用服务
Dubbo能做什么?
1.透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
2.软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
- 服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。
Dubbo 缺省协议采用单一长连接和 NIO 异步通讯。
在dubbo分布式中,序列化传输使用的hessian方式进行序列化:只传成员属性值和值的类型,不传方法或静态变量
服务上线怎么不影响旧版本?
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
在低压力时间段,先升级一半提供者为新版本
再将所有消费者升级为新版本
然后将剩下的一半提供者升级为新版本
Dubbo的超时重试:
Dubbo的超时重试机制为服务容错、服务稳定提供了比较好的框架支持 dubbo在调用服务不成功时,默认会重试2次。Dubbo的路由机制,会把超时的请求路由到其他机器上,而不是本机尝试,所以 dubbo的重试机器也能一定程度的保证服务的质量。,但是在一些比较特殊的网络环境下(网络传输慢,并发多)可能由于服务响应慢,Dubbo自身的超时重试机制(服务端的处理时间超过了设定的超时时间时,就会有重复请求)可能会带来一些麻烦。
常见的应用场景故障: 1、发送邮件(重复) ;2、账户注册(重复).。
解决方案: 对于核心的服务中心,去除dubbo超时重试机制,并重新评估设置超时时间。
(1)、去掉超时重试机制
<dubbo:provider delay="-1" timeout="6000" retries="0"/>
(2)、重新评估设置超时时间
<dubbo:service interface="*.*" ref="*" timeout="延长服务时间"/>
dubbo的核心配置:配置应用信息,注册中心相关信息,服务协议,暴露服务,引用服务
<?xml version="1.0" encoding="UTF-8"?>
<!-- 添加 DUBBO SCHEMA -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 应用名 -->
<dubbo:application name="dubbodemo-provider"/>
<!-- 连接到哪个本地注册中心 -->
<dubbo:registry id="dubbodemo" address="zookeeper://localhost:2181"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="28080"/>
<!-- 声明需要暴露的服务接口 -->
<dubbo:service registry="dubbodemo" timeout="3000" interface="com.chanshuyi.service.IUserService" ref="userService"/>
</beans>
分布式
分布式的目的:
1.高可用:主机挂掉后,自动故障转移,使前端服务对用户无影响
2.读写分离:将主机读压力分流到从机上,可实现负载均衡,提高效率
分布式系统通信可采用面向消息的中间件ActiveMQ,RabbitMQ等
分布式计算可采用mapreduce
分布式存储是可用Hbase,redis,mongoDB
分布式监控可用zookeeper:zookeeper是开源的分布式应用程序协调服务,核心是原子广播,这个机制保证了各个Server之间的同步,实现这个机制的协议叫Zap协议,有两种模式:恢复模式(选主)和广播模式(同步),服务启动或主机宕机时,采用恢复模式,选出主机,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
zookeeper是如何保证事务的顺序一致性的?
zookeeper采用了递增的事务Id来标识,所有的proposal(提议)都在被提出的时候加上了zxid,zxid实际上是一个64位的数字,高32位是epoch(时期; 纪元; 世; 新时代)用来标识leader是否发生改变,如果有新的leader产生出来,epoch会自增,低32位用来递增计数。当新产生proposal的时候,会依据数据库的两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行。
CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼
● 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
● 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
● 分区容错性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
主从复制用单向链表更稳定,master和slave在同一局域网内时连接更稳定
项目
注册登录:
验证焦点,得到焦点时隐藏错误信息,失去焦点时显示错误信息。
Ajax异步请求,Servlet调用service调用dao查询数据库是否存在
激活
userServlet#activation()
1.获取激活码2.把激活码交给service的activation(string)来完成激活3.保存成功信息,转发到msg.jsp显示
userService#activation(String)
1.使用激活码查询user2.是null抛出异常,不是null查看user的status是否为true3.不是true修改user的status为true
调用dao通过激活码查询用户,修改用户状态
redis缓存:
最近浏览
1.用户id做key,商品Id做value,以list存入缓存
2.为保证唯一性用lrem将list中已浏览的商品Id去掉,再加入
3.在lpush到list后,将前60个数据之外用LTrim修剪掉
4.用expire(key,60 * 60 * 24 * 30)添加缓存失效时间30天
购物车
购物车用hashmap,一个外键一个内部键,外部键对应购物车,内部键对应商品,查购物车数据可以从redis中查询。
订单信息
1.用sortedset,分数为下单时间+订单后三位进行排序
2.数据更新成功缓存更新失败时的数据不一致通过发mq的策略进行缓存更新尝试
在service中调用事务提交订单,用户下单,库存相应减少,如果减少库存失败,该订单也撤销
易宝特点:1.支付用Https 2.用GBK编码 3.大小写敏感
static关键字
1.修饰成员变量和方法,让他们成为类的成员属性和方法,一个对象如果对static修饰的属性做了改变,其他的对象都会受到影响;修饰方法时,可以使用"类名.方法名"的方式操作方法,避免了先要new出对象的繁琐和资源消耗。
2.静态代码块,当我们初始化static修饰的成员时,可以将他们统一放在一个以static修饰的代码块中。
CountDownLatch,ThreadLocal,CyclicBarrier
CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行,通过计数器实现,每当线程完成任务后,计数器减1,到达0时,表示所有线程完成任务,在闭锁上等待的线程就可以恢复执行任务。
ThreadLocal:提供线程内部的局部变量,在本线程随时随地可取,隔离其他线程
CyclicBarrier:栅栏允许两个或多个线程在某个集合点同步
HTTP中GET与POST的区别
最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。
由于GET把参数包含在URL。而(大多数)服务器最多处理64K大小的url,所以该请求有长度限制。
GET和POST还有一个重大区别,
简单的说:
GET产生一个TCP数据包;POST产生两个TCP数据包。
长的说:
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
状态码
Mysql集群原理和优缺点
MySQL 群集分为三种节点:管理节点,数据节点和SQL节点。
管理节点:主要用于管理各个节点,能够通过命令对某个节点进行重启、关闭、启动等操作。也能够监视全部节点的工作状态。
数据节点:主要是对数据的存储,不提供其他的服务。
SQL节点:主要是对外提供SQL功能,类似一台普通的 MySQL Server。
而SQL节点和数据节点可以是同一台机器,也就是说这台机器即是SQL节点也是数据节点。它们只是逻辑关系上的划分,实际部署时,甚至所有的阶段都可以位于同一台物理机器上,只是配置较复杂些。
优缺点
优点:
99.999 %的高可用性
快速的自动失效切换
灵活的分布式体系结构,没有单点故障
高吞吐量和低延迟
可扩展性强,支持在线扩容
缺点:
存在很多限制,比如:不支持外键,数据行不能超过8K(不包括BLOB和text中的数据)
部署、管理、配置很复杂
占用磁盘空间大,内存大
备份和恢复不方便
重启的时候,数据节点将数据load到内存需要很长时间
分布式session一致性
保证session一致性的架构设计常见方法:
session同步法:多台web-server相互同步数据。缺点:占内网带宽
客户端cookie存储法:一个用户只存储自己的数据。缺点:有大小限制,占外网带宽,不安全
后端统一存储:web-server重启和扩容,session也不会丢失(建议的方式)
线程池
//www.greatytc.com/p/179113d1454b
接口如何处理重复请求?
最好用的:基于redis缓存的计数器由于数据库的操作比较消耗性能,了解到redis的计数器也是原子性操作 ,而且能提升qps的峰值。 每次request进来则新建一个以orderId为key的计数器,然后+1。 如果>1(不能获得锁): 说明有操作在进行,删除。 如果=1(获得锁): 可以操作。 操作结束(删除锁):删除这个计数器。
缓存穿透与缓存雪崩
什么是缓存穿透?
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1.对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。2.采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
什么是缓存雪崩?
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
3:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期(此点为补充)