java笔记--问题总结

1. 垃圾回收算法

标记-清除算法

标记-清除算法是最基本的算法,和他的名字一样,分为两个步骤,一个步骤是标记需要回收的对象。在标记完成后统一回收被标记的对象。这个算法两个问题。一个是效率问题,标记和清除的效率不高。第二个问题是标记-清除之后会有大量不连续的碎片空间,如果我们需要更大的连续内存就必须GC

复制算法

复制算法,不同于标记-清除,复制算法大多数用于新生代,它需要大小相等的两块内存,每次只使用一块内存,当GC的时候会把这块内存存活的对象复制到另外一块内存上面,解决了时间效率和空间碎片问题。在新生代中会把他分为三个内存一个Eden 两个Survivor默认是8比1,开始会使用一个Eden和一个Surivivor装载内存,清除时会将这两个保留的对象都保存另外在Survivor中,并且年龄加1(以后提升为老年代),如果超出了Survivor中的限制会用老年代的内存担保。

标记-整理算法

主要特点是,解决了碎片问题。标记整理算法,标记过程和第一算法一样,但是他处理的时候会让存活的对象向一边移动解决了空间碎片问题,用于老年代的处理。

分代收集算法

分代收集算法不是新思想,只是把上面的算法结合起来了。

2. AtomicInteger实现原理

AtomicInteger使用value来保存值,value是volatile的,保证了可见性。

对于get方法直接返回value,对于自增一或者添加值使用了CAS自旋锁,使用了一个死循环,如果cas返回为true就可以退出循环。对于CAS 全称是compare and swap比较和交换,CAS需要三个操作数,一个是变量内存地址,一个是excepted过期值,一个是现在要更新的值,我们操作的时候仅当V符合旧预期的值的时候才能更新我们新的。对于它的自增的操作,首先是卸载一个for循环里面然后获得当前的值,给这个值+1,然后进行cvs要是失败会再次Get()最新值再次写.

参考:http://ifeve.com/atomic-operation/

3. synchronizedlock的区别主要有三个区别

用法区别,性能区别,锁机制的区别。

对于用法区别:synchronized可以在方法中上使用也可以在特定代码块中使用,括号中表示需要锁的对象,如果在方法上就是对该对象的锁,如果是在类的方法上就是类的锁,使用Lock必须自己用代码显示申明何时开启锁,何时关闭锁。synchronized是jvm的底层实现,而Lock是由代码执行。

对于性能的区别:ReentrantLock 功能上要多于synchronized,多了锁投票,定时锁等。如果在小规模的竞争上synchronized效率比较高,如果在大规模的竞争上synchronize就比较低而Lock基本不变、

锁的机制也不同:synchronized获得锁和释放锁都是在块中,都是自动释放,不会引起死锁,而Lock需要自己定位释放,不然会引起死锁。在Lock中也使用了tryLock方法用非阻塞的方式获取锁。

在lock中用一个锁变量和队列维护同步。

gc停顿原因,如何降低GC停顿

原因:gc停顿的意思就像是在整个分析期间冻结在某个时间点上,具体的原因是防止在分析的时候,对象引用关系还在不断的变化,如果没有GC停顿很有可能分析不准确。

如何降低:在Serial的老年代垃圾收集器中,会把所有线程的暂停,停下来收集哪些是死亡对象。在CMS和G1中都采取了初始标记、并发标记、短暂GC停顿重新标记,初始标记会直接记录能GC ROOTS 关联的对象,在并发标记的时候有一个线程来标记,这个时候对象的发生的变化都会记录下来,在重新标记的时候会修正,这样就会降低GC停顿时间

jvm如何调优,参数怎么调?如何利用工具分析jvm状态?

合理的分配内存,分配栈和堆的内存,在堆中我们还可以详细划分新生代和老年代的内存比例,在新生代中我们也可以划分Eden和Surivior的内存比例(调该比例大小),合理的划分内存区域大小,可以帮助我们jvm调优,我们采取合适的垃圾回收器,比如在新生代启用serial垃圾回收器,在老年代采用cms并发标记,可以降低GC停顿,当然也可以尝试去采用G1垃圾回收器

jvm中类加载过程

类加载到类被卸载过程包括7个阶段

1.加载

通过类的全限定名把类文件的二进制流加入进来,通过这个字节流(这个二进制流也是我们代理类的方法),然后通过这个二进制流把静态存储结构转化为运行时方法区的结构(不包括类变量,类变量在准备阶段),在内存中生成一个Class对象,作为方法区访问的入口。

2.验证验证是验证Class文件的字节流包含的信息是否符合当前虚拟机的要求规范,防止恶意攻击、

3.准备在方法区为类变量分配内存和设置初始值,这个时候的初始值是数据的0值,不是我们定义的值,如果是常量的话准备阶段就会设置为我们定义的值

4.解析将符号引用(这里的符号引用指的是字面量的形式,只需要无歧义地定位到目标)替换为直接变量

5.初始化类初始化阶段是我们加载过程的最后一步,执行类构造器,合并static语句,有static的顺序决定。

6.使用

7.卸载

4. Springbean的加载机制,bean生成具体步骤

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

spring中Bean的加载机制其实就是IOC容器的初始化,比如我们这里定义了一个IOC容器,BeanFactroy的子类ClassXMLPathApplicationContext,在他的构造函数中我们会把xml路径写进去这个步骤就是定位,接下来就是BeanDefiniton的载入,在构造函数当中有一个refresh()的函数,这个就是载入BeanDefinition的接口,这个方法进去之后是一个同步代码代码块,把之前的容器销毁和关闭创建了一个BeanFatroy,就像对我们的容器重新启动一样,然后我们对BeanDefiniton载入和解析解析完毕之后会把beanDefinitionbeanName放入BeanFactoryHashMap中维护。在这里Bean已经被创建完成,然后我们就像IOC容器索要Bean,如果是第一次索要会触发依赖注入,会递归的调用gebBean实现依赖出入.

5. 讲下java锁的原理

对于synchronized关键字,在jvm中在编译的时候在同步块的前后形成监视进入和监视退出两个字节码,这两个字节码都需要一个引用类型的参数来指定要锁定和解锁的对象,如果指定了的话就使用指定的对象,如果没有指定看是类方法还是对象方法来决定,在执行监视进入的指令的时候,会判断能否进入,进入成功之后会把锁计数器加1,如果不成功就会继续等待和其他的线程竞争,出锁的时候会把锁计数器减1变为0,也就是释放了锁。在这里要说明一点java的线程时映射到系统原生线程之上,如果要阻塞或者唤醒一个线程都需要操作系统帮忙,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多的处理器时间。有可能比用户的代码执行时间还长。在jdk1.6之后对synchronized优化是非常的号的,比如锁粗化,锁自旋,锁消除。轻量级锁和偏向锁。

而对于ReentrantLock是代码上的实现

6. 线程和进程的区别

进程是一段正在执行的程序,线程也叫作”轻量级进程“,他是程序执行的最小单元,一个进程可以有多个线程,各个线程之间共享程序的内存空间(比如说堆空间)及一些进程级的资源,进程和进程之间不能共享内存只能共享磁盘文件,线程也有4中状态:就绪,运行,挂起,死亡。

(新版本)进程是程序执行时的一个实例,从内核的观点看,进程的目的就是担当分配系统资源的基本单位。

线程是进程的一个执行流,是cpu调度和分配的基本单位,它是比进程更小的能独立运行的基本单位。一个进程由几个线程组成,线程和同属一个进程的其他的线程共享进程所拥有的全部资源。

进程-资源分配的最小单位。线程-程序执行的最小单位。

进程由独立的空间地址,线程没有单独的地址空间同一进程内的共享进程的地址空间,只有自己独立的堆栈和局部变量。对于我们来说实现一个多线程的任务比实现一个多进程的任务好,

7. 为什么分配线程比分配一般的对象更需要花费更大的代价?

首先他的资源非常“节俭”。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段,堆栈段和数据段,这是一种“昂贵”的多任务工作方式。而运行一个进程中的多个进程,他们彼此之间使用相同的地址空间,共享进程内的大部分数据,启动一个进程的时间远大于一个线程的时间。

(1)地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;

(2)资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源

(3)线程是处理器调度的基本单位,但进程不是.

8. Spring AOP是怎么实现

首先简单说一下Spring AOP几个比较重要的名词:

通知:定义在连接点做什么,为切面增强提供织入接口,意思就是说增强的内容以及增强的方式

切点:PointCut:决定通知应用于什么方法上或者类上。(点:方法或者类)

通知器:连接切点和通知结合起来,可以让我们知道那个通知应用于哪个结点之上,这个结合为应用使用Ioc容器配置AoP应用

开始我们使用AOP的时候是用得java编写的切面通知使用XML配置,后面我们摒弃了采用@AspectJ注解对其进行标注

然后AOP的实现原理有一个ProxyFactoryBean(代理工厂),这个ProxyFactoryBea是在Spring Ioc环境之中,创建AOP应用的最底层。在ProxyFactoryBean中我们会配置好通知器Advisor,在ProxyFactory需要为目标对象生成代理对象。ProxyFactory有一个getObject方法,在我们IOC容器中如果获取这个bean会自动调用这个方法,首先第一步初始化通知器链,通知器链只会初始化一次,使用标志位判断,遍历通知器,把所有通知器加入拦截器链,接下来就是代理对象的生成,利用目标对象以及拦截器我们可以正确的生成代理对象,这里生成代理对象有两种方法一种是jdk一种是cglib,在得到AopProxy代理对象之后,我们首先会根据配置来对拦截器是否与当前的调用方法想匹配,如果当前方法匹配就会发挥作用,他会遍历Proxy代理对象中设置拦截器链的所有拦截器,拦截器调用完成之后才是目标对象的调用,这个时候会有一个注册机制,在拦截器中运行的拦截器,会注册,我们就不需要再判断。Aop的源代码中也大量使用了IOC容器,比如从IOC中找到通知器。

9. SpringMVC的主要流程

首先我们会在web.xml中配置DispathcerServlet,这个就是SpringMVC的入口,DispathcerServlet的父类FrameWorkServlet首先会初始化WebApplicationContext,DispacherServlet初始化了9个组件,初始完毕后我们开始进入,FramkeworkServlet中做了三件事一个是调用doService模板方法具体处理请求。将当前所有的请求都合并在一个方法里面和我们的httpServlet做法有点不同,在DispathcerServlet中有一个doService的方法,其中调用了doDispatch这也是最核心的首先根据request找到Handler,根据Handler找到了HderAdapter,用HandlerAdapter处理Handler其中包含一些参数的处理,处理完成后就行方法调用之后得到结果然后把View渲染给用户或者把数据发给用户。

详细版本:

1.输入一个网址,比如http请求,首先我们tomcat服务器,会对请求创建出我们request和response,然后就交给我们对应的servlet处理。

10. 创建线程方式

实现runnable接口重写run方法,继承Thread,利用线程池来创建。

11. 想让所有线程都等到一个时刻同时执行有哪些方法

CountDownLatch:CountDownLatch首先我们在构造函数当中传入了一个标志值,然后在需要阻塞的地方调用await(),直到其他线程把countDown减少完。这个是不可重用的。

CyclicBarrier:和他的名字一样栅栏,我们对他的构造函数传入一个栅栏值,在需要阻塞的地方调用await的时候我们就对其基础值加一,直到等于栅栏值。调用CylicBarrier的reset方法可以对他进行重置。

Semaphore信号量:Semaphore可以同时控制访问的线程个数,如果需要这个资源我们会acquire()以阻塞的方式去请求,如果没有可用的信号量,就等待,release释放信号量,这个机制有点类似于锁。

jdk1.7中提供了一个同步器Phaser,作用和countdownLatch,CyclicBarrier类似,但PHaser的使用方式更为灵活。使用register注册方法递增计数器,使用arriveAndDeregister()方法来递减计数器,使用arriveAndAwaitAdvane()方法阻塞线程,当计数器归0唤醒。

12. volatile的作用

参看博客:http://blog.csdn.net/libing13820393394/article/details/48582999

第一:volatile是Java虚拟机提供的最轻量级的同步机制,使变量对所有的线程可见,保证了可见性,但是并不能保证它的原子性。

第二个:禁止指令重排序优化。普通变量仅仅保证在该方法所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行一样。从硬件的方面来说,并不是指令任意重拍,他只是把多条指令不安程序规定顺序分发给电路处理单元,比如说2*3+5 2*3之间是有依赖,5就可以排到他们前面。volatile会帮助我们加入内存屏障防止重排序。volatile读操作性能消耗与普通变量几乎没区别,写操作会慢一些,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

注意:对于volatile修饰的变量,jvm只是保证从主内存加载到线程的工作的内存是最新的

13. 谈一谈java内存模型

(1)java虚拟机规范试图定义一种JAVA内存模型来屏蔽掉各种硬件和操作系统的内存访问的差异。

(2)java内存模型的主要目标是定义程序中各个变量的访问规则,这里的变量不包含局部变量和方法参数,而是指的是实例字段、静态字段、和构成数组对象的元素。

(3)java内存模型规定了所有的变量都存储在主内存中,而线程内的局部变量在自己的工作内存中,并且还有被该线程使用到的变量的主内存 的副本拷贝,线程对变量的操作(读取、赋值)都在工作内存中进行,不能直接读写主内存的变量,不同的线程无法直接访问对方工作内存的变量,线程键的变量值的传递需要通过主内存来完成,在内存模型中比较重要的就是工作线程和主内存的交互。

内存之间的交互:

java内存模型定义的操作:

Lock (锁定)

Unlock(解锁)

Read(读取)

Load(载入)

Use(使用)

Assign(赋值)

Store(存储)

Write(写入)

变量从主内存到工作内存:按照顺序执行read load操作

变量从工作内存到主内存:按照顺序执行Store write操作

重排序:

包括:编译器优化重排序、指令级并行重排序、内存系统重排序

14. 什么时候使用LinkedList?

首先分写LinkedList和ArrayList的不同,在经常插入和删除的时候,在实现栈和队列的时候,不适合随机查找元素。

15. Object有哪些方法(九大方法),clone是深复制还是浅复制,finalize一般在什么时候使用:

wait,notify,notifyall,clone,getclass,toString,equals,hashcode,finalize。

1、Clone()方法

private保护方法,实现对象的浅复制,只有类实现了Clonable接口才可以调用该方法,否则抛出CloneNotSupportException。clone是浅复制,复制完成后其中的变量引用还是和以前的一样,如果要实现深复制需要我们把所有的变量引用都递归复制一次,然后再赋值。(或者额使用序列化,也可以实现深拷贝)如果我们要自己实现clone()方法必须要实现克隆接口clonable。

2、Equals()方法

在object中与==是一样的,子类一般需要重写该方法

3、hashCode()方法

该方法用于哈希查找,重写了equals方法一般都要重写hashcode方法,这个方法在一些具有哈希功能的collection中使用

4、getClass()方法

final方法,获得运行时的类型

5、Wait()方法

使得当前的线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。Wait方法会一直等待,直到获得锁(到了睡眠的时间间隔也会唤醒自己)或者被中断掉。

调用该方法,当前的线程会进入到睡眠的状态,直到调用该对象的notify方法、notifyAll方法、调用interrupt中断该线程,时间间隔到了。

6、Notify()方法

唤醒在该对象上的等待的某个线程

7、notifyAll()方法

唤醒在该对象上的等待到所有的线程

8、toString()方法

把对象转换成string类型进行输出

9、finalize()方法

finalize在我们垃圾回收器回收这个对象的时候工作,可以做一些后续的工作,即进行一些必要的清理和清除的工作,比如说关闭流。当然我们也可以在这个里面对我们即将被回收的对象逃出回收。这里需要注意的是系统只会调用一次finalize()方法。但是一般我们不推荐使用这个方法,因为这个方法是为了对开始C和C++程序员的一种妥协,因为C中有析构函数,这个方法运行代价高,不确定大,我们还是会推荐使用try{}finally,他做的方法try{}finally都可以做。

16. 如何管理线程(主要介绍各种线程池的实现)

使用线程池来管理线程

Java中实现多种线程池

我们使用executors工厂产生我们的线程池,当线程池达到负载的时候会在我们线程池管理的Runnable阻塞队列中等待,不会像线程那样竞争CPU

第一种 newFixedThreadPool,和它的名字一样这是一个固定线程池,我们可以设置基本大小也就是我们没有任何任务执行的时候的大小,最大大小,只有在工作队列满了才能达到最大大小。

第二种 newCachedThreadPool这种线程池把大小设置为Integer.MAX_VALUE,基本大小设置为0,空闲超时设置1分钟,这种线程池可以无限扩展,并且当需求降低时会自动收缩。

第三种 newSingleThreadPool这种线程池把基本大小设置为1,最大大小都设置为1,只允许同一时刻一个线程。

固定线程池和单线程池固定默认使用的是阻塞队列无界的LinkedBlockingQueue,在这个阻塞队列中可以无限增长。但是对于我们的newCachedThreadPool来说他的线程池是无限大的,不需要阻塞等待,我们这里使用的是SynchronousQueue来避免排队,其实这个东西不是一个队列,是直接在线程之间进行移交,当线程池的大小小于所需要的时候,要么创建一个要么拒绝一个。我们一般在使用的时候可以扩展,使用使用信号量来控制提交速率。

17. 如何让线程A等待线程B结束

1.使用join方法可以等待A线程结束,或者单线程池中 阻塞队列的方式让A先获得单线程池的线程,然后B一直阻塞,知道A释放自己的线程。

18. 如何优化jvm参数

,首先设置堆的大小,一般设置xmx和xms大小相同,如果老年代容易溢出可以扩充老年代,也要适当的调整永久代大小,选择自己合适的收集器,调整新生代对象年龄阀值等。

19. 什么是守护线程

线程会分为两种:

普通线程和守护线程。在JVM启动时创建的所有线程中,除了主线程其他都是守护线程,比如说垃圾回收器就是守护线程,当普通线程全部退出的时候守护线程也会退出,我们自己也可以手动设置手动线程在线程启动之前,但是我们应该尽可能少使用守护线程,因为我们很少有操作可以在不进行清理就可以安全地抛弃,比如说I/O操作。

20. TCP如何控制拥塞

拥塞控制就是防止过多的数据注入网络中,这样可以使网络中的路由器或链路不致过载。

发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。

为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。ssthresh的用法如下:

cwnd时,使用慢开始算法。也就是乘法算法

cwnd>ssthresh时,改用拥塞避免算法。也就是加法算法

cwnd=ssthresh时,慢开始与拥塞避免算法任意。

当出现拥塞的时候就把心的门限值设为此时窗口大小的一般,窗口大小设置为1,再重新执行上面的步骤。

当收到连续三个重传的时候这就需要快重传和快恢复了,当收到连续三个重传这个时候发送方就要重传自己的信息,然后门限减半但是这个时候并不是网络阻塞,窗口只会减半执行拥塞避免算法。

21. ThreadLoacl?

我们使用ThreadLocal为每个使用该类型的变量提供了一个独立的副本,具体的实现是在每个线程中保存了一个ThreadLocalMap,这个ThreadLoaclMap会在我们第一次使用ThreadLoal中的set方法创建出来,set方法就是保存在ThreadLocalMap中,该变量为key,值为value,get方法也从这个HashMap中找。

22. OSI网络模型?

网卡在哪一层(物理层)

交换机在哪一层(链路层)

路由器在哪一层(网络层)

传输TCP

会话 SQL

表示 IMG

html在应用层

23. HTTP1.0Http1.1区别?

1.0默认是多次tcp连接多次请求,然后增加了keep alive功能,但是必须在request Header手动增加Connection:keepalive

1.1是一次tcp连接多次请求,新的persistence功能

24. POSTGET方法的区别?

长的说:

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。

因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?

1. GET与POST都有自己的语义,不能随便混用。

2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

1.get是从服务器上获取数据,post是向服务器传送数据。

2.get是通过URL来传递数据,POST是通过表单传递,因此get数据限制在1024k,而POST没有限制

3.在java服务器端get是通过request.qureySting post通过request.getParameterNames和reque.getParameterValue

4.get是安全的,幂等的 POST即不安全又不幂等(多次操作和一次操作一样)

在rest中设计的话,一般get用来查询数据,POST用来添加数据,PUT用来更新数据,Delete用来删除数据

25. filter执行顺序?

多个filter的执行顺序是web.xml中的配置顺序

影响SQL执行效率的因素?

1.is null和is not null

2.通配符的like

3.order by

4.not

5.in和exists

GBK和UTF-8的区别

GBK包含全部中文字符; UTF-8则包含全世界所有国家需要用到的字符。

GBK的文字编码是双字节来表示的,即不论中、英文字符均使用双字节来表示,只不过为区分中文,将其最高位都定成1。

至于UTF-8编码则是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码。对于英文字符较多的论坛则用UTF-8节省空间。

UTF8是国际编码,它的通用性比较好,外国人也可以浏览论坛 GBK是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大~

26. stringBufferStringBuilder

1.三者在执行速度方面的比较:StringBuilder >  StringBuffer  >  String

servlet和Filter的实现原理

StringBuffer是线程安全的,St不是线程安全的,内部的实现是使用char数

27. 什么是rest

一次网站访问的全过程:

楼主提到TCP/IP分层的时候用的是网络接口层,那么楼主的TCP/IP分层概念应该是:应用层、传输层、网络层、网络接口层(包含了七层模型中的数据链路层和物理层)。

我尝试回答一下楼主的问题,希望大家继续拍砖,如果访问www.163.COM这个网站,那么他的主要过程应该是:

一、主机向DNS服务器发起域名解析请求,以得到相对应的IP地址

二、应用层应用HTTP协议发送数据

三、数据到达传输层封装成数据段,主机使用1024以后的随机源端口号,目标端口号为80

四、数据段到达网络层封装成数据包,加入主机源IP地址和目标IP地址

五、数据包到达网络接口层首先封装成数据帧,加入源MAC地址和目标MAC地址(注:此目标MAC地址为本地网关的MAC地址,源和目的MAC地址在数据转发的过程中,会由路由器不断的改变)。封装后将数据转换为物理层的数据流,通过互联网发送至目标服务器。

28. 什么时候抛出InvalidMonitorStateException异常?为什么?

调用 wait ()/notify ()/notifyAll ()中的任何一个方法时,如果当前线程没有获得该对象的锁,

那么就会抛出 IllegalMonitorStateException 的异常

也就是说程序在没有执行对象的任何同步块或者同步方法时,

仍然尝试调用 wait ()/notify ()/notifyAll ()时。由于该异常是 RuntimeExcpetion 的子类,

所以该异常不一定要捕获(尽管你可以捕获只要你愿意

作为 RuntimeException,此类异常不会在 wait (),notify (),notifyAll ()的方法签名提及。

29. Collections.synchronizedXX 方法的原理

返回了一个同步容器,在这个同步容器中的所有方法都有一个锁为当前对象或者指定锁的同步块,用这种阻塞同步的方法可以让我们容器同步

30. 什么是Future

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

1.cancel方法用来取消任务

2.isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

3.isDone()表示是否完成

4.get()获得执行结果,这个方法会一直阻塞

5.在时间范围内获取执行结果

FutureTask是Future的实现类

31. 说出数据连接池的工作机制是什么?

J2EE服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。

   调用:客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数决定。

释放:当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可以使用这个连接。

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素:

1) 最小连接数是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费;

2) 最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。

3) 如果最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。

32. 存储过程和函数的区别

存储过程是用户定义的一系列sql语句的集合,涉及特定表或其它对象的任务,用户可以调用存储过程,而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表。

33. 事务是什么?

事务是作为一个逻辑单元执行的一系列操作。

34. 游标的作用?如何知道游标已经到了最后?

游标用于定位结果集的行,通过判断全局变量@@FETCH_STATUS可以判断是否到了最后,通常此变量不等于0表示出错或到了最后。

35. 系统进程间通信的方式

管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

命名管道 (named pipe) 命名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

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

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

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

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

套接字( socket ) 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

36. jvm调优:内存溢出和内存泄露:

溢出解决:

1.在代码中减少不必要的实例构造

2.设置堆和永久代的大小 -xms堆最小 -xmx堆最大

内存泄露:

内存泄露不能通过配置解决代码的问题。比如资源在使用完毕后没有释放,一些对象存在无效引用我们不能回收。

37. httphttps的区别

http协议是无状态的明文传输,Https而SSL+HTTP协议构建的可进行加密传输。https的服务器必须向CA申请一个证明服务器用途的证书,而客户端通过该证书确认服务器,所以银行都是https,所有的通讯都是在密钥加密的情况下,而密钥则是通过证书交换,所以第三方拦截的数据没有密钥也没有用。

SSL用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取及窃听。

38. 虚拟机性能监控状况

jps:显示系统内所有进程的信息。

jstat:收集虚拟机各种运行状态信息的命令行工具。 -gc,监视java堆 -class 就是监视类加载,还可以监视编译状况。

jinfo:java配置信息工具。

jmap:用于生成堆转储快照 有些选项只能在linux下才能看见。

jhat:配合jmap。

jstack:堆栈追踪。

39. Servlet生命周期

1.加载:在Servlet容器启动的时候会通过类加载器加载我们的Servlet的Class文件。

2.创建:在创建过程的时候如果没有在web.xml文件中使用load-on-startup我们在第一次访问我们的Servlet的时候会初始化实例,如果配置了这个并且大于1会在容器启动的时候就创建。

3.初始化:init()初始化的方法只会被调用一次。在我们实例化被创建之后就会执行初始化。

4.处理客户请求:service()在Servlet的service方法中会根据不同的http方法来调用。

5.卸载:destroy()当我们Servlet需要被卸载的时候就会调用我们的destory()方法,随后垃圾回收器会回收。

40. Minor GCFULL GC

当我们需要向新生代中分配内存时出现不足的情况:会出现Minor GC,在新生代中 都是朝生夕灭的对象,频率比较高。Minor GC发生在新生代。

FULL GC指在老年代发生的GC,一般情况下出现了FULL GC都会伴随着一次Minor GC。

为什么MinorGC 和FULL GC速度有差距呢?

在Minor GC中使用的copy算法,在FullGC中使用的是标记清除 或者标记整理算法。

copy算法是用空间换时间 mark(标记)和copy(复制)是一个动作。

但是mark-sweap或mark-compact都是分为两个阶段,先标记再清除是两步骤。

所以Minro GC速度会快于FullGC。

41. JVM调优问题

对于JVM调优的重点是垃圾回收和内存管理。

垃圾回收我们可以使用我们的cms垃圾回收器。

对于内存管理有:

永久代溢出、栈溢出、堆溢出的情况

永久代溢出:

针对永久代溢出在JVM默认情况只有64M的永久代大小,很多东西都需要我们永久代区内存,尤其是使用Spring等框架的时候会有cglib的动态字节码生成class,都会存储在我们的永久代。所以我们需要扩充永久代防止内存溢出。

堆溢出:

对于堆溢出我们也是比较常见的比如说我们使用容器的时候没有释放内存很有可能就会导致堆溢出,需要动态扩展。

栈溢出:

对于栈我们也可以设置提高。

42. 单例模式

1)恶汉式的单例模式

利用静态static的方式进行实例化,在类被加载时就会创建实例。

/**

 * 饿汉式实现单例模式

 */

public class Singleton {

  private static Singleton instance = new Singleton();//在类加载时实例单例对象

    private Singleton() {//私有的构造器

    }

    public static Singleton getInstance() {//返回一个实例对象

        return instance;

    }

}

(2)懒汉式实现单例模式

在被第一次引用时才去创建对象。

/**

 * 懒汉式实现单例模式

 */

public class Singleton {

    private static Singleton instance;//创建私有的静态变量

    private Singleton() {//私有的构造函数

    }

        // synchronized方法,多线程情况下保证单例对象唯一

public static synchronized Singleton getInstance() {

//如果实例对象为空,就重新去实例化

        if (instance == null) {

            instance = new Singleton();

        }

        return instance;

    }

}

分析:这中方法的实现,效率不高,因为该方法定义为同步的方法。

(3)双重锁实现的单例模式double check

/**

 * DCL实现单例模式

 */

public class Singleton {

    private static valotile Singleton instance = null;//这里要加入valotile关键字,避免指令重排序,  可能先赋值但是没有分配内存

    private Singleton() {

    }

    public static Singleton getInstance() {

        // 两层判空,第一层是为了避免不必要的同步

        // 第二层是为了在null的情况下创建实例

        if (instance == null) {

            synchronized (Singleton.class) {

                if (instance == null) {

                    instance = new Singleton();

                }

            }

        }

        return instance;

    }

}

分析:资源的利用率较高,在需要的时候去初始化实例,而且可以保证线程的安全,该方法没有去进行同步锁,效率比较好。

(4)静态内部类实现单例模式

/**

 * 静态内部类实现单例模式

 */

public class Singleton {

    private Singleton() {

}

//返回实例的方法

    public static Singleton getInstance() {

        return SingletonHolder.instance;

    }

    /**

     * 静态内部类

     */

private static class SingletonHolder {

    //静态私有的实例对象

        private static Singleton instance = new Singleton();

    }

}

分析:第一次加载类时不会去初始化instance,只有第一次调用getInstance()方法时,虚拟机才会加载内部类,初始化instance

可以保证线程的安全,单例对象的唯一,延迟了单例的初始化。

(5)枚举单例

/**

 * 枚举实现单例模式

 */

public enum SingletonEnum {

    INSTANCE;//直接定义一个实例对象

    public void doSomething() {

        System.out.println("do something");

    }

}

分析:枚举实例的创建是线程安全的,即使反序列化也不会生成新的实例,在任何的情况下都是单例的。

43. 设计模式6大原则

1.单一职责。2.里氏替换 3.依赖导致4接口隔离5.迪米特法则6.开闭原则。

44. XMLJSON优缺点

XML的优点

A.格式统一,符合标准;

B.容易与其他系统进行远程交互,数据共享比较方便。

      C.可读性高

XML的缺点

 A.XML文件庞大,文件格式复杂,传输占带宽;

 B.服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;

 C.客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;

 D.服务器端和客户端解析XML花费较多的资源和时间。

JSON的优缺点

JSON的优点:

A.数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;

B.易于解析,客户端JavaScript可以简单的通过eval()进行JSON数据的读取;

      C.支持多种语言,包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析;

D.在PHP世界,已经有PHP-JSON和JSON-PHP出现了,偏于PHP序列化后的程序直接调用,PHP服务器端的对象、数组等能直接生成JSON格式,便于客户端的访问提取;

E.因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。

JSON的缺点

A.没有XML格式这么推广的深入人心和喜用广泛,没有XML那么通用性;

B.JSON格式目前在Web Service中推广还属于初级阶段。

       C:可读性低。

45. 四种读取XML文件读取的办法

1.DOM生成和解析XML文档

为XML文档的已解析版本定义了一组接口。解析器读入整个文档,然后构建一个驻留内存的树结构。

优点:整个文档树在内存中,便于操作;支持删除,修改,重新排列等。

缺点:  把整个文档调入内存,存在很多无用的节点,浪费了时间和空间。

2.SAX为解决DOM

1、边读边解析,应用于大型XML文档

2、只支持读

3、访问效率低

4、顺序访问

3.DOM4J生成和解析XML文档(解析工具) 性能最好 SUM的JAXM也大量采用DOM4J

HIBERNATE采用DOM4J

虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一种智能分支。它合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项,它通过DOM4J API和标准DOM接口具有并行访问功能。从2000下半年开始,它就一直处于开发之中。

    为支持所有这些功能,DOM4J使用接口和抽象基本类方法。DOM4J大量使用了API中的Collections类,但是在许多情况下,它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是,虽然DOM4J付出了更复杂的API的代价,但是它提供了比JDOM大得多的灵活性。

    在添加灵活性、XPath集成和对大文档处理的目标时,DOM4J的目标与JDOM是一样的:针对Java开发者的易用性和直观操作。它还致力于成为比JDOM更完整的解决方案,实现在本质上处理所有Java/XML问题的目标。在完成该目标时,它比JDOM更少强调防止不正确的应用程序行为。

    DOM4J是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,特别值得一提的是连Sun的JAXM也在用DOM4J.

4.JDOM

JDOM

优点:①是基于树的处理XML的Java API,把树加载在内存中

        ②没有向下兼容的限制,因此比DOM简单

        ③速度快,缺陷少

        ④具有SAX的JAVA规则

缺点:

     ①不能处理大于内存的文档

     ②JDOM表示XML文档逻辑模型。不能保证每个字节真正变换。

     ③针对实例文档不提供DTD与模式的任何实际模型。

     ④不支持与DOM中相应遍历包。

最适合于:JDOM具有树的便利,也有SAX的JAVA规则。在需要平衡时使用

46. 如何防止Sql注入

有两种办法

1.第一种消毒,通过正则匹配过滤请求数据中可能注入的SQL。

2.使用预编译手段preparedStatemengt。

47. DB第一范式,第二范式,第三范式

第一范式:没一列属性不可再分,没有多值属性

第二范式:在符合第一范式的基础上,存在主键

第三范式:在符合第二范式的基础上,非关键字独立于其他的非关键字,并且依赖关键字。不能存在传递依赖。

48. publicprotectedprivate默认权限

private:用于修饰类和方法,只允许该类访问。

默认:只允许在同一个类和同一个包中进行访问。

protected:用于修饰类和方法,允许该类和子类访问以及同一个包中访问。

public:用于修饰类和方法,允许该包下面和其他包的访问,即在全局范围都可以访问。

49. 数据库事务

事务的特性:

原子性:事务是不可再分的;

一致性:事务的实行前后,数据库的状态保持一致;

隔离性:事务的并发访问,事务之间的执行互不干扰;

持久性:事务结束后数据永久保存在数据库中。

什么是脏读?

脏读就是一个事务读取了该数据并且对该数据做出了修改,另一个事务也读取了该修改后的数据但是前一个事务并没有提交,这是脏数据。

读取到保存在数据库内存中的数据。

什么是不可重复读?

一个事务:在同一个事务中读取同一数据,得到的内容不同。一个事务读取另外一个事务更新的数据,导致二次的查询的数据不一致。

什么是幻读?

幻读是当事务不独立发生的。比如一个事务删除了所有数据,另一个事务又插入了一条,那么第一个事务的用户会发现表中还没有修改的数据行。一个事务读取到另外一个事务提交的数据,导致查询的结果不一致的问题。

数据库的隔离级别:

Read uncommitted:未提交读:三中都有可能发生

Read committed :已提交读 避免脏读

Repeated read:重复读:避免脏读 不可重复读

Serializable:串行化读   都可以避免

50. WebService到底是什么

一言以蔽之:WebService是一种跨编程语言和跨操作系统平台的远程调用技术。

所谓跨编程语言和跨操作平台,就是说服务端程序采用java编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上运行。

所谓远程调用,就是一台计算机a上的一个程序可以调用到另外一台计算机b上的一个对象的方法,譬如,银联提供给商场的pos刷卡系统,商场的POS机转账调用的转账方法的代码其实是跑在银行服务器上。再比如,amazon,天气预报系统,淘宝网,校内网,百度等把自己的系统服务以webservice服务的形式暴露出来,让第三方网站和程序可以调用这些服务功能,这样扩展了自己系统的市场占有率,往大的概念上吹,就是所谓的SOA应用。

其实可以从多个角度来理解WebService,从表面上看,WebService就是一个应用程序向外界暴露出一个能通过Web进行调用的API,也就是说能用编程的方法通过Web来调用这个应用程序。我们把调用这个WebService的应用程序叫做客户端,而把提供这个WebService的应用程序叫做服务端。从深层次看,WebService是建立可互操作的分布式应用程序的新平台,是一个平台,是一套标准。它定义了应用程序如何在Web上实现互操作性,你可以用任何你喜欢的语言,在任何你喜欢的平台上写Web service ,只要我们可以通过Web service标准对这些服务进行查询和访问。

   WebService平台需要一套协议来实现分布式应用程序的创建。任何平台都有它的数据表示方法和类型系统。要实现互操作性,WebService平台必须提供一套标准的类型系统,用于沟通不同平台、编程语言和组件模型中的不同类型系统。Web service平台必须提供一种标准来描述Web service,让客户可以得到足够的信息来调用这个Web service。最后,我们还必须有一种方法来对这个Web service进行远程调用,这种方法实际是一种远程过程调用协议(RPC)。为了达到互操作性,这种RPC协议还必须与平台和编程语言无关。

51. java中锁的优化

1.减少锁持有的时间,可以减少其它线程的等待时间,不能让一个线程一直控制着某个锁不释放,导致竞争加剧。

2.减少锁的粒度,合适的锁的代码块,可以减少竞争,控制锁的范围。

3.锁分离,将锁安功能划分,比如读写锁,读读不互斥,读写互斥,写写互斥,保证了线程的安全,提高了性能。比如阻塞队列中的take和put

4.锁粗化,如果对同一个锁不停的进行请求,同步和释放,这个消耗是非常的大的,所以适当的时候可以粗化。

5.锁消除,编译器可以帮助我们优化比如一些代码根本不需要锁。

52. 虚拟机内的锁优化

1.偏向锁:偏向当前已经占有锁的线程,在无竞争的时候,之前获得锁的线程再次获得锁时,会判断是否偏向锁指向我,那么该线程将不用再次获得锁,直接进入同步块。

2.轻量级锁:偏向锁失败后,利用cas补救补救失败就会升级为重量级锁。

3.自旋锁:会做空操作,并且不停地尝试拿到这个锁。

53. java中一亿个数找前10000个最大的

先利用Hash法去重复,去除大量的之后 然后等量的分成100 用小顶堆 来获得10000个,再把所有的1万个都合在一起就OK

54. java中线程的状态

java中的线程的状态有5种(新建、就绪、运行、阻塞、结束)

1.新建:创建后尚未启动的线程处于这种状态,新建出一个线程对象。

2.就绪状态:当针对该对象掉用了start()方法,该线程等待获取CPU的使用权

3.运行状态:在就绪状态下,获得了CPU处于运行状态。

4.阻塞:

等待阻塞:运行的线程执行wait方法,JVM会把该线程放入等待池

同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其他的线程锁占用,则jvm会把该线程放入锁池中。

其他阻塞:运行的线程在执行sleep()方法或者join()方法时,或者发出IO请求,JVM会把线程置为阻塞状态。

5.结束:

也就是我们的死亡,表明线程结束。

55. Maven的生命周期

maven有三套相互独立的生命周期

1.clean生命周期

pre-clean,clean,post-clean

2.default生命周期构建项目

1.validate:验证工程是否正确,所有需要的资源是否可用

2.compile:编译项目源代码

3.test:使用合适的单元框架来测试已编译的源代码。

4.Package:把已编译的代码打包成可发布的格式,jar。

4)Package:把已编译的代码打包成可发布的格式,比如jar。

5)integration-test:如有需要,将包处理和发布到一个能够进行集成测试的环境。

6)verify:运行所有检查,验证包是否有效且达到质量标准。

7)install:把包安装到maven本地仓库,可以被其他工程作为依赖来使用。

8)Deploy:在集成或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。

3.

site生命周期:建立和发布项目站点,phase如下

1pre-site:生成项目站点之前需要完成的工作

2)site:生成项目站点文档

3)post-site:生成项目站点之后需要完成的工作

4)site-deploy:将项目站点发布到服务器

数据库索引

56. 什么是索引?

1)索引是对记录集多个字段进行排序的方法。

2)也是一个数据结构,在一张表中为一个字段创建索引,将创建另外一个数据结构,包含字段的数值以及指向相关记录的指针,就可以对该数据结构进行二分法排序,当需要查询时就可以降低时间复杂度。

优势:快速存取数据;保证数据记录的唯一性;实现表和表之间的参照完整性;在使用order by group by子句进行数据的检索时,利用索引可以减少排序和分组的时间。

弊端:建立索引表也是会需要额外的空间。

57. 索引的工作原理:

在对表中记录进行搜索时并不是对表中的数据进行全部的扫描遍历,而是查看在索引中定义的有序列,一旦在索引中找到了要查询的记录,就会得到一个指针,它会指向相应的表中数据所保存的位置。

58. 索引的类型:

1)聚集索引:数据页在物理上的有序的存储,数据页的物理顺序是按照聚集索引的顺序进行排列。在聚集索引中数据页聚集索引的叶子节点,数据页之间通过双向的链表形式相连接,实际的数据存储在叶节点中。

2)非聚集索引:叶子节点不存放具体的数据页信息,只存放索引的键值。非聚集索引的叶子节点包含着指向具体数据的指针,数据页之间没有连接,是相对独立的。

3)唯一索引:在整个表中仅仅会出现一次(主键约束/UNIQUE)

(4)非唯一索引:在提取数据时允许出现重复的值。

(5)单一索引和组合索引

59. 哪些情况下索引会失效?

1.条件中有or但是前后没有同时使用索引

2.多列索引,不是使用前面部分

3.like查询是以%开头

4.字符类型应该加单引号 防止转换为int类型

60. 数据库查询优化(Sql)

1应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

2、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where order by 涉及的列上建立索引。

3、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

     select id from t where num is null

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

     select id from t where num=0

4、尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

     select id from t where num=10 or num=20

可以这样查询:

     select id from t where num=10

     union all

     select id from t where num=20

 5、下面的查询也将导致全表扫描:(不能前置百分号)

     select id from t where name like ‘�c%’

若要提高效率,可以考虑全文检索。

 6、in not in 也要慎用,否则会导致全表扫描,如:

     select id from t where num in(1,2,3)

对于连续的数值,能用 between 就不要用 in 了:

     select id from t where num between 1 and 3

 7、如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

     select id from t where num=@num

可以改为强制查询使用索引:

     select id from t with(index(索引名)) where num=@num

 8、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

     select id from t where num/2=100

应改为:

     select id from t where num=100*2

 9、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

     select id from t where substring(name,1,3)=’abc’–name以abc开头的id

     select id from t where datediff(day,createdate,’2005-11-30′)=0–’2005-11-30′生成的id

应改为:

     select id from t where name like ‘abc%’

     select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′

 10、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

 11、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,并且应尽可能的让字段顺序与索引顺序相一致。

 12、不要写一些没有意义的查询,如需要生成一个空表结构:

     select col1,col2 into #t from t where 1=0

这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:

     create table #t(…)

 13、很多时候用 exists 代替 in 是一个好的选择:

     select num from a where num in(select num from b)

用下面的语句替换:

     select num from a where exists(select 1 from b where num=a.num)

 14、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

 15、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert update 的效率,因为 insert update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。

 16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

 17、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

 18、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

 19、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

 20、尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

 21、避免频繁创建和删除临时表,以减少系统表资源的消耗。

 22、临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使 用导出表。

 23、在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

 24、如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

 25、尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

 26、使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

 27、与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

 28、在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

 29、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

 30、尽量避免大事务操作,提高系统并发能力。查询sql语句中哪些比较慢

1.慢查询日志,一般设置查询超过两秒就记录

2.processlist:显示哪些线程正在运行.

3.explain关键字可以让我们更好的优化

61. 数据库设计优化:

1.反范式设计,尽量是单表,可以高效利用索引

2.可以使用查询缓存,Mysql也会自带查询缓存

3.尽量查询通过搜索引擎查不通过我们

4.key,value数据库

62. 锁的优化策略

1.读写分离,锁分离

2.减少锁持有时间,可以减少其他的锁的持有时间

3.以正确的顺序获得和释放锁

4.适当的锁的范围扩大或者缩小,控制锁的粒度

63. Spring Bean中作用域

singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例。

prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。

request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效。

session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效。

Globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效。

64. java中启定时任务

1.利用sleep特性//休眠

2.time和timerTask//定时器

3.ScheduledExecutorService service.scheduleAtFixedRate(runnable, 10, 1, TimeUnit.SECONDS);

 //任务调度服务

65. 操作系统如何进行分页调度

用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。

66. linux内核的三种主要调度策略:

1SCHED_OTHER 分时调度策略,

2,SCHED_FIFO实时调度策略,先到先服务

3,SCHED_RR实时调度策略,时间片轮转

67. TCPUDP相关

74.TCP通过什么方式提供可靠性:

1.超时重发,发出报文段要是没有收到及时的确认,会重发。

2.数据包的校验,也就是校验首部数据和。

3.对失序的数据重新排序

4.进行流量控制,防止缓冲区溢出

5.快重传和快恢复

6.TCP会将数据截断为合理的长度

68. TCPUDP的区别:

1.UDP是无连接的,TCP必须三次握手建立连接

2.UDP是面向报文,没有拥塞控制,所以速度快,适合多媒体通信要求,比如及时聊天,支持一对一,一队多。多对一,多对多。

3.TCP只能是一对一的可靠性传输

TCP的RPC,在协议栈的下层,能够灵活的对字段进行定制,减少网络传输字节数,降低网络开销,提高性能,实现更大的吞吐量和并发数。但是实现代价高,底层复杂,难以得到开源社区的支持,难以实现跨平台

76.集群调优

1.load

load是被定义为特定时间间隔内运行队列中的平均线程数,uptime查看,一般load不大于3,我们认为负载是正常的,如果每个CPU的线程数大于5,表示负载就非常高了。

2.CPU利用率

查看cpu的消耗的情况命令:top | grep Cpu

查看磁盘的剩余空间命令:df -h

查看系统的内存的使用情况:free -m

69. 心跳检测方法

1.使用ping命令

对于full gc导致不响应,网络攻击这种 ping展示不明确

2.使用curl

访问我们的自测地址

3.对不同的功能使用curl检测,在response中加入状态头,表示正常

可以计算qps通过28原则

innodb存储引擎通过预写事务日志的方式保障事务的原子性,也就是在写入数据之前,先将数据操作写入日志,这种成为预写日志

轻量级锁认为在程序运行过程中,绝大部分的锁,在整个同步周期内都是不存在竞争的,利用cas操作避免互斥开销。

偏向锁是jdk1.6中引入的一项优化,甚至可以省掉CAS操作,偏向锁偏向第一个获得他锁的线程,如果在接下来执行过程中,这个锁没有被其他线程获取,则持有偏向锁的线程永远不需要同步。

70. GC调优

查看GC日志,根据GC日志来优化

我们可以通过jps找到我们虚拟机正在运行的进程。参数 通过jps -help了解。

Jstat -gc 或者 -gcutil 查看堆使用情况-class 配合Jps得到进程

BTrace原理利用hotspot虚拟中的热替换,把代码动态的替换到java程序内,可在不需要重启的时候就可以排除问题

JConsole

我们也可以使用JConsole来分析这是一个图形化的,比较容易让我们操作

使用VisualVM 进行远程连接 使用JMX方式,也有修改tomcat的catalina就行了

71. 内部类去访问外部变量,为什么需要加final?

题目有点问题,并不是所有的外部变量才加final,我们的内部类访问我们的成员变量就不需要加final,但是访问局部变量就必须要加final,因为方法(main方法)结束我们栈帧也就销毁了,但是我们内部类在堆中并没有被销毁,如果引用了成员变量,这时候被销毁了肯定是不行的,所以我们就需要成员变量设置为final,让其在方法(main方法)结束时不会被销毁。

72. 泛型

泛型的作用:在我们没有泛型的时候,我们通过对Object的引用来对参数的任意化,任意化有个缺点就是要做显示的强制类型转换,强制转换有一个不好的地方是运行的时候才会报错,泛型的好处实在编译的时候检查类型安全,所以泛型的特点就是简单安全,泛型的原理是类型擦除,java的泛型是伪泛型,在编译期间,所有的泛型信息都会被擦除掉。在生成的java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器编译的时候去掉。比如List

73. nginxapache的对比

1.nginx相对于apache来说

(1轻量级占用的内存少;

(2)抗并发,nginx是异步非阻塞,apache是阻塞的,在高并发的情况下,nginx的性能优;

(3)高度模块化的设计,编写模块相对简单;

(4)社区活跃,各种高性能模块有。

适合场景:apache适合于动态的请求,而负载均衡和静态请求适合nginx,nginx采用的是epoll,并且有自己的一套sendfile系统,减少在内存中的赋值次数。

2.apache 相对于nginx 的优点:

(1)rewrite ,比nginx 的rewrite 强大

(2)模块超多,基本想到的都可以找到;

(3)少bug ,nginx 的bug 相对较多

(4)超稳定。

74. 线程、进程共享和独立

共享的部分:

1.进程代码段

2.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)

3.进程打开的文件描述符、

4.信号的处理器、

5.进程的当前目录

6.进程用户ID与进程组ID

线程独有的内容包括:

1.线程ID

2.寄存器组的值

3.线程的堆栈

4.错误返回码

5.线程的信号屏蔽码

最大的优势就是线程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,线程的性能优势就越明显。

第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在线程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

75. 简单的说一下nginx的优点

1.作为高性能的web服务器:相比Apache,Nginx使用更少的资源,支持更多的并发连接,体现更高的效率,这点让Nginx受到虚拟主机提供商的欢迎。一个Nginx实例能够轻松支持高达5万并发。

2.作为负载均衡服务器:Nginx即可以在内部直接支持PHP,也可以支持作为HTTP代理服务器对外进行服务,独有的send files系统,减少文件复制次数

3.作为邮件代理服务器:也比Apache好很多。

4.Nginx安装简单,配置文件非常简洁。启动容易,7*24小时几乎不间断,可以进行热更新。

76. BIO

在BIO中读和写都是同步阻塞的,阻塞的时间取决于对方I/O线程的处理速度和网络的传输速度。本质上来讲,我们是无法保证生产环境的网络状况和对端的应用程序可以足够快,应用程序是不应该依赖对方的处理速度,它的可靠性就非常差。BIO就算用线程池实现,要是所有可用线程都被阻塞到故障点中,后续的所有I/O消息都将在队列中排队。

77. NIO

1)提供了高速,面向块的I/O

2)在NIO中所有数据都是用缓冲区来处理的,也就是使用我们的jvm中的direct memory(直接内存)。缓冲区是一个数组,但是缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置等信息。

3)在NIO中channel(通道)也是特别重要的他是我们数据读写的通道,一般来说流比如inputStream和outputStream都是单向的,而通道是双向的,是全双工的

4)多路复用器Selector也是比较重要的,掌握它对于我们的NIO编程来说是比较重要的。多路复用器提供选择已经就绪的任务的能力。Selector会不断轮训注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,通过SelectionKey可以获取就绪Channel的集合,进行后续的I/o操作。我们只需要一个线程就可以管理我们多个客户端。

78. 垃圾收集器

1.Serial收集器

    Serial收集器是JAVA虚拟机中最基本、历史最悠久的收集器,在JDK 1.3.1之前是JAVA虚拟机新生代收集的唯一选择。Serial收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个 CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。

     Serial收集器到JDK1.7为止,它依然是JAVA虚拟机运行在Client模式下的默认新生代收集器。它也有着优于其他收集器的地方:简单而高 效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集 效率。在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌面应用基本上不会 再大了),停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生,这点停顿是可以接受的。所以,Serial收集器对于运行在 Client模式下的虚拟机来说是一个很好的选择。

2. Parallel(并行)收集器

这是 JVM 的缺省收集器。就像它的名字,其最大的优点是使用多个线程来通过扫描并压缩堆。串行收集器在GC时会停止其他所有工作线程(stop-the- world),CPU利用率是最高的,所以适用于要求高吞吐量(throughput)的应用,但停顿时间(pause time)会比较长,所以对web应用来说就不适合,因为这意味着用户等待时间会加长。而并行收集器可以理解是多线程串行收集,在串行收集基础上采用多线 程方式进行GC,很好的弥补了串行收集的不足,可以大幅缩短停顿时间(如下图表示的停顿时长高度,并发比并行要短),因此对于空间不大的区域(如 young generation),采用并行收集器停顿时间很短,回收效率高,适合高频率执行。

3.CMS收集器

    CMS(Concurrent Mark Sweep)收集器是基于“标记-清除”算法实现的,它使用多线程的算法去扫描堆(标记)并对发现的未使用的对象进行回收(清除)。整个过程分为6个步骤,包括:

初始标记(CMS initial mark)

并发标记(CMS concurrent mark)

并发预清理(CMS-concurrent-preclean)

重新标记(CMS remark)

并发清除(CMS concurrent sweep)

并发重置(CMS-concurrent-reset)

其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一 般会比初始标记阶段稍长一些,但远比并发标记的时间短。其他动作都是并发的。

需要注意的是,CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序的运行自然还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次 收集中处理掉它们,只好留待下一次GC时再将其清理掉。这一部分垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,即还需要预留足够的 内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使 用。在默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在应用中老年代增长不是太快,可以适当调高参数 -XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数以获取更好的性能。要是CMS运行期 间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CMSInitiatingOccupancyFraction设置 得太高将会很容易导致大量“Concurrent Mode Failure”失败,性能反而降低。

try catch有return 在字节码中 可以看到会出现Goto跳转行数,跳转到finally中的return

79. Tomcat总结构

最外层的Server提供接口访问内部的Service服务。

Service服务的作用就是把Connector 在Connetor中监听端口和Container连接起来,方便我们的操纵控制。

在tomcat中生命周期是统一的由Lifecycle来管理的

在lifecycle中有两个方法比较重要start和stop,调用server的start会去调用自己下面所有service的start方法

Connector最重要的功能就是接受连接请求然后分配线程处理

在Container 4个级别Engine,Host,Context,warpper,这四个组件不是平行的,而是父子关系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。通常一个 Servlet class 对应一个 Wrapper,如果有多个 Servlet 就可以定义多个 Wrapper,如果有多个 Wrapper 就要定义一个更高的 Container 了,如 Context。在Host中有个value比较重要,类似于一个管道,和拦截器链差不多,我们在中间可以进行一些处理。

Engine中只能添加子容器Host,不能添加父容器.Engine下可以配置多个虚拟主机Virtual Host,每个虚拟主机都有一个域名

当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理

一个Host就类似于一个虚拟主机,用来管理应用。代表一个Virtual Host,虚拟主机,每个虚拟主机和某个网络域名Domain Name相匹配

每个虚拟主机下都可以部署(deploy)一个或者多个Web App,每个Web App对应于一个Context,有一个Context path

当Host获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理

匹配的方法是“最长匹配”,所以一个path==""的Context将成为该Host的默认Context

所有无法和其它Context的路径名匹配的请求都将最终和该默认Context匹配。

Context 代表 Servlet 的 Context,它具备了 Servlet 运行的基本环境,理论上只要有 Context 就能运行 Servlet 了。简单的 Tomcat 可以没有 Engine 和 Host。

Context 最重要的功能就是管理它里面的 Servlet 实例,Servlet 实例在 Context 中是以 Wrapper 出现的,还有一点就是 Context 如何才能找到正确的 Servlet 来执行它呢?

一个Context对应于一个Web Application,一个Web Application由一个或者多个Servlet组成

Context在创建的时候将根据配置文件" role="presentation" style='font-size:14px;font-style:normal;font-weight:normal;color:rgb(0, 0, 0);'>CATALINAHOME/conf/web.xml和WEBAPP_HOME/WEB-INF/web.xml载入Servlet类

当Context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的Servlet类

如果找到,则执行该类,获得请求的回应,并返回.

80. 线程池参数

JDK1.5中引入了强大的concurrent包,其中最常用的莫过了线程池的实现。ThreadPoolExecutor(线程池执行器),它给我们带来了极大的方便,但同时,对于该线程池不恰当的设置也可能使其效率并不能达到预期的效果,甚至仅相当于或低于单线程的效率。

ThreadPoolExecutor类可设置的参数主要有:

(1)corePoolSize  基本大小

核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。

核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。

(2)maxPoolSize  最大大小

当线程数大于或等于核心线程corePoolSize,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。

 keepAliveTime  大于coolPoolSize 会退出

当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。

 allowCoreThreadTimeout  是否退出核心线程

是否允许核心线程空闲退出,默认值为false。

 queueCapacity

任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。

81. 线程池按以下行为执行任务

当线程数小于核心线程数时,创建线程。

当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

当线程数大于等于核心线程数,且任务队列已满

若线程数小于最大线程数,创建线程

若线程数等于最大线程数,抛出异常,拒绝任务

系统负载

参数的设置跟系统的负载有直接的关系,下面为系统负载的相关参数:

 tasks,每秒需要处理的最大任务数量

 tasktime,处理每个任务所需要的时间

 responsetime,系统允许任务最大的响应时间,比如每个任务的响应时间不得超过2秒。

参数设置

corePoolSize:

每个任务需要tasktime秒处理,则每个线程每钞可处理 1/tasktime个任务。系统每秒有tasks个任务需要处理,则需要的线程数为:tasks/(1/tasktime),即 tasks*tasktime个线程数。假设系统每秒任务数为100~1000,每个任务耗时0.1秒,则需要100*0.1至1000*0.1,即 10~100个线程。那么corePoolSize应该设置为大于10,具体数字最好根据8020原则,即80%情况下系统每秒任务数,若系统80%的情 况下第秒任务数小于200,最多时为1000,则corePoolSize可设置为20。

queueCapacity:

任务队列的长度要根据核心线程数,以及系统对任务响应时间的要求有关。队列长度可以设置为(corePoolSize/tasktime)*responsetime (20/0.1)*2=400,即队列长度可设置为400。

队列长度设置过大,会导致任务响应时间过长,切忌以下写法:

LinkedBlockingQueue queue = new LinkedBlockingQueue();

这实际上是将队列长度设置为Integer.MAX_VALUE,将会导致线程数量永远为corePoolSize,再也不会增加,当任务数量陡增时,任务响应时间也将随之陡增。

maxPoolSize:

当系统负载达到最大值时,核心线程数已无法按时处理完所有任务,这时就需要增加线程。每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-queueCapacity)* (20/200),即60个线程,可将maxPoolSize设置为60。

keepAliveTime:

线程数量只增加不减少也不行。当负载降低时,可减少线程数量,如果一个线程空闲时间达到keepAliveTiime,该线程就退出。默认情况下线程池最少会保持corePoolSize个线程。

allowCoreThreadTimeout:

默认情况下核心线程不会退出,可通过将该参数设置为true,让核心线程也退出。

以上关于线程数量的计算并没有考虑CPU的情况。若结合CPU的情况,比如,当线程数量达到50时,CPU达到100%,则将 maxPoolSize设置为60也不合适,此时若系统负载长时间维持在每秒1000个任务,则超出线程池处理能力,应设法降低每个任务的处理时间 (tasktime)。

使用ThreadLocal解决SimpleDateFormat,

privatestatic ThreadLocal threadLocal = new ThreadLocal() {        @Override        protected DateFormat initialValue() {            returnnew SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        }    };

或者推荐Joda-Time处理时间比较推荐

在java线程中有6个状态也就是Thread中的枚举类

NEW,RUNNABLE,WAITING,TIME_WAITING,BLOCKED,TERMINATED

应该在try{} catch{}中重新设置中断状态,因为发出中断异常被退出了。

82. java内存模型

jvm规范定了jvm内存模型来屏蔽掉各种操作系统,虚拟机实现厂商和硬件的内存访问差异,确保java程序在所有操作系统和平台上能够实现一次编写,到处运行的效果。

83. 为什么IP协议也能够进行数据的不可靠传输,还需要Udp

1.我们需要端口号来实现应用程序间的区分

2.UDP校验和可以实现传输层的校验,虽然UDP协议不具备纠错能力,但是可以对出错的数据包进行丢弃,而IP的校验只是在校验IP报头,而不是整个数据包,整个数据包的校验是在传输层完成的,如果出错了,就会把出错的数据包丢弃。这也就是为什么需要有传输层

84. 进程通信中管道和共享内存谁的速度快?

1.管道通信方式的中间介质是文件,通常称这种文件为管道文件。两个进程利用管道进行通信,一个进程为写进程,另一个进程为读进程。写进程通过往管道文件中写入信息,读进程通过读端从管道文件中读取信息,这样的方式进行通信。

2.共享内存是最快的可用IPC形式,他通过把共享的内存空间映射到进程的地址空间,进程间的数据传递不在通过执行任何进入内核的系统调用,节约了时间。java内存模型就采用的是共享内存的方式。各个进程通过公共的内存区域进行通信。

85. java线程池shutdownshutdownNow的区别?

Shutdown()方法

当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException(拒绝执行异常)异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。

shutdownNow()方法

根据JDK文档描述,大致意思是:执行该方法,线程池的状态立刻变成STOP状态并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。

它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。

86. Integer的装箱和拆箱的基本原理

Integer包装类是Java最基本的语法糖优化,如果我们写一段程序,通过反编译就会看到,通过的是Integer.valueOf()或者Interger.intValue()来进行转换的。

Integer和int比较会自动拆箱,

当Integer和integer比较的时候是不会自动拆箱的,除非遇到了算术符,才会调用intValue()方法去拆箱。并且在Integer中的equals是不会处理数据类型转换关系的,使用时是需要慎用,在equals方法中判断为Integer类的才会执行真正的判断流程也就是拆箱去判断,所以不会处理数据类型转换比如Long。如果是基本类型比较,编译器会隐形的把int转换为long,就可以得出正确的结论。

87. 为什么不推荐使用resumesuspend

因为在使用suspend()去挂起线程的时候,suspend在导致线程暂停的同时,不会去释放任何锁的资源。必须要等待resume()操作,被挂起的线程才能继续。如果我们resume()操作意外地在suspend()前就执行了,那么被挂起的线程可能很难有机会被继续执行。并且,更严重的是:所占用的锁不会被释放,因此可能会导致整个系统工作不正常

yield是谦让,调用后会使当前线程让出CPU,但是注意的地方让出CPU并不表示当前线程不执行了。当前线程在让出CPU后,还会进行CPU资源的争夺,有可能刚刚一让马上又进。

88. 数据库事务的隔离级别

1.未提交读 都不能解决

2.已提交读 能解决脏读

3.可重复读 能解决脏读,不可重复读

4.序列化读 能解决脏读,不可重复读,幻读

89. Docker和虚拟机的比较

1.传统的虚拟机在宿主机操作系统上面会利用虚拟机管理程序去模拟完整的一个虚拟机操作系统,docker只是在操作系统上的虚拟化,直接复用本地主机的操作系统,非常轻量级。

docker启动速度一般在秒级,虚拟机启动速度一般在分钟级。

2.对于资源的使用一般是mb,一台机器可以有上千个docker容器,但是虚拟机占用资源为GB,只能支持几个。

3.性能:接近原生,由于又虚拟化了一层所以效率低。

4.docker采用类似git的命令学习升本低,指令简单。

5.虚拟机隔离性是完全隔离,容器是安全隔离

90. lucence组件

每一个词都会有一个倒排表,多个可以合并

为了标识webSocket:会在请求头中写一个upgrade:webSocket

HTTP比较

同样作为应用层的协议,WebSocket在现代的软件开发中被越来越多的实践,和HTTP有很多相似的地方,这里将它们简单的做一个纯个人、非权威的比较:

相同点

都是基于TCP的应用层协议。

都使用Request/Response模型进行连接的建立。

在连接的建立过程中对错误的处理方式相同,在这个阶段WS可能返回和HTTP相同的返回码。

都可以在网络中传输数据。

不同点

 WS使用HTTP来建立连接,但是定义了一系列新的header域,这些域在HTTP中并不会使用。

 WS的连接不能通过中间人来转发,它必须是一个直接连接。

 WS连接建立之后,通信双方都可以在任何时刻向另一方发送数据。f

 WS连接建立之后,数据的传输使用帧来传递,不再需要Request消息。

 WS的数据帧有序。

91. 微服务架构

首先看一下微服务架构的定义:微服务(MSA)是一种架构风格,旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦。它有如下几个特征:

小,且只干一件事情。

独立部署和生命周期管理。

异构性

轻量级通信,RPC或者Restful。

92. BIO通信模型图:

BIO以是一客户端一线程,一个Acceptor线程来接受请求,之后为每一个客户端都创建一个新的线程进行链路处理,最大的问题缺乏弹性伸缩能力,客户端并发访问量增加后,线程数急剧膨胀。可以用线程池缓解但是还是不行。

NIO非阻塞IO解决了这个问题,一个线程就可以管理多个Socket.

在NIO中有三个需要我们了解Buffer,Channel,Selector

Buffer:NIO是面向缓冲区,IO是面向流的。缓冲区实质上一个数组

,缓冲区不仅仅是一个数组,缓冲区提供了对数据结构化访问以及维护读写位置等信息。

通道Channel:

Channel是一个通道,网络数据通过Channel读取和写入。通道与流的不同之处在于通道是双向的,流是一个方向上移动,通道可以用于读写操作,特别是在UNIX网络编程模型底层操作系统的通道都是全双工的,同时支持读写操作。

Selector:多路复用器NIo编程的基础,多路复用器提供选择就绪任务的 能力。简单来说Selector会不断注册在其上的Channel,如果某个Channel上面发生读或者写时间,这个Channel就处于就绪状态,会被Selector轮询出来,通过SelectionKey,一个多路复用器可以同时轮询多个Channel,JDK使用了epoll代理传统select实现,所以没有最大连接句柄fd的限制,意味着一个线程负责Selector的轮询,就可以接入成千上万的客户端

select/poll epoll select poll顺序扫描fd,就绪就返回fd。epoll则是 采用事件驱动,用回调的方式返回fd。

93. 面向对象设计七大原则

1. 单一职责原则(Single Responsibility Principle

每一个类应该专注于做一件事情。

2. 里氏替换原则(Liskov Substitution Principle

超类存在的地方,子类是可以替换的。

3. 依赖倒置原则(Dependence Inversion Principle

实现尽量依赖抽象,不依赖具体实现。

4. 接口隔离原则(Interface Segregation Principle

应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。

5. 迪米特法则(Law Of Demeter

又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。

6. 开闭原则(Open Close Principle

面向扩展开放,面向修改关闭。

7. 组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP

尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。

94. 拦截器和过滤器的区别

强类型:不允许隐形转换

弱类型:允许隐形转换

静态类型:编译的时候就知道每一个变量的类型,因为类型错误而不能做的事情是语法错误.

动态类型:编译得时候不知道每一个变量的类型,因为类型错误而不能做的事情是运行时错误

编译语言和解释语言:解释性编程语言,每个语句都是执行的时候才翻译而且是一句一句的翻译就很低。

编译的语言就只需要一次就可以了

95. 继承Thread 接口Runnabel的区别

主要是继承和实现接口的两个区别,如果只想重写run方法就可以使用,如果不重写其他方法 就使用runnable,如果使用实现接口的实现,让自己方便管理线程以及让线程复用,可以使用线程池去创建。

96. hash算法(特点、哈希函数构造、解决冲突的策略)

哈希表的概念:

哈希表就是一种以键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值。

哈希表的实现思路:

如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。对于冲突的情况,则需要处理地址的冲突问题。所以,一方面要构造出良好的哈希函数,对键值集合进行哈希,另外一方面需要设计出良好的解决冲突的算法,处理哈希碰撞的冲突。

哈希表的查找步骤:

(1)使用哈希函数将被查找的键转换为数组的索引。在理想的情况下,不同的键会被转换为不同的索引值,但是在有些情况下我们需要处理多个键被哈希到同一个索引值的情况。所以哈希查找的第二个步骤就是处理冲突

(2)处理哈希碰撞冲突。有很多处理哈希碰撞冲突的方法,本文后面会介绍拉链法线性探测法

哈希表的思想:

是一个在时间和空间上做出权衡的经典例子。如果没有内存限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为O(1);如果没有时间限制,那么我们可以使用无序数组并进行顺序查找,这样只需要很少的内存。哈希表使用了适度的时间和空间来在这两个极端之间找到了平衡。只需要调整哈希函数算法即可在时间和空间上做出取舍。

哈希表的工作步骤:

 1)   哈希(Hash)函数是一个映象,即将关键字的集合映射到某个地址集合上,它的设置很灵活,只要这个地址集合的大小不超出允许范围即可;

 2)  由于哈希函数是一个压缩映象,因此,在一般情况下,很容易产生“冲突”现象,即: key1!=key2,而  f  (key1) = f(key2)。键不同,但是对应的取值相同。

3).  只能尽量减少冲突而不能完全避免冲突,这是因为通常关键字集合比较大,其元素包括所有可能的关键字,而地址集合的元素仅为哈希表中的地址值。

哈希函数的构造方法:

1、直接地址法

以数据元素的关键字k本身或者他的线性的函数作为它 的哈希地址,也就是H(k)=k,或者H(k)=a*k+b;

适用的场景:地址集合的大小==关键字的集合。

2、数字分析法

取数据元素关键字中某些取值较均匀的数字位作为哈希地址的方法

适用的场景:能预先估计出全体关键字的每一位上各种数字出现的频度。

3、折叠法

将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位),这方法称为折叠法

适用场景:关键字的数字位数特别多。

4、平方取中法

先取关键字的平方,然后根据可使用空间的大小,选取平方数是中间几位为哈希地址。

适用场景:通过取平方扩大差别,平方值的中间几位和这个数的每一位都相关,则对不同的关键字得到的哈希函数值不易产生冲突,由此产生的哈希地址也较为均匀。

5、减去法

6、基数转换法

7、除留余数法

8、随机数法

9、随机乘数法

10、旋转法

构造哈希哈希函数的原则:

1、计算哈希函数的时间

2、关键子的长度

3、哈希表的长度

4、关键字的分布的情况

5、记录查找频率

哈希函数的冲突解决的方法:

1、开放地址法

这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。这种方法有一个通用的再散列函数形式:

          Hi=(H(key)+di)% m   i=1,2,…,n

其中H(key)为哈希函数,m 为表长,di称为增量序列。增量序列的取值方式不同,相应的再散列方式也不同。主要有以下三种:

l         线性探测再散列

    dii=1,2,3,…,m-1

这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。

l         二次探测再散列

    di=12,-12,22,-22,…,k2,-k2    ( k<=m/2 )

这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。

l        伪随机探测再散列

    di=伪随机数序列。

2、再哈希法

这种方法是同时构造多个不同的哈希函数:

    Hi=RH1(key)  i=1,2,…,k

当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。

3、链地址法

这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。

4、建立公共溢出区

这种方法的基本思想是:将哈希表分为基本表溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

97. 类加载的方式

1.通过new

2.通过反射利用当前线程的classloader

3.自己继承实现一个classloader实现自己的类装载器

class.forName Classloader.loadClass区别

Classloader

104.HashMap内部是怎么实现的?(拉链式结构)

98. 核心:hashMap采用拉链法,构成“链表的数组”

存储示意图:

99. 索引index建立规则:

1)一般情况下通过hash(key)%length实现,元素存储在数组中的索引是由key的哈希值对数组的长度取模得到。

2)hashmap也是一个线性的数组实现的,里面定义一个内部类Entry,属性包括key、value、next.Hashmap的基础就是一个线性数组,该数组为Entry[] ,map里面的内容都保存在entry[]数组中。

(3)确定数组的index:hashcode%table.length

数组的下标index相同,但是不表示hashcode相同。

实现随机存储的方法:

// 存储时:

int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值

int index = hash % Entry[].length;

Entry[index] = value;

 

// 取值时:

int hash = key.hashCode();

int index = hash % Entry[].length;

return Entry[index];

put方法的实现:

如果两个key通过hash%Entry[].length得到的index相同,会不会有覆盖的危险?

这里HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方, 第一个键值对A进来,通过计算其key的hash得到的index=0,记做:Entry[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,Entry[0] = B,如果又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。所以疑问不用担心。也就是说数组中存储的是最后插入的元素

get方法的实现:

先定位到数组元素,再遍历该元素处的链表

table的大小:

table的初始的大小并不是initialCapacity,是initialCapacity的2的n次幂

目的在于:当哈希表的容量超过默认的容量时,必须重新调整table的大小,当容量已经达到最大的可能的值时,这时需要创建一张新的表,将原来的表映射到该新表。

req.getSession().invalidate(); 销毁session

req.getSession().setMaxInactiveInterval(30); 设置默认session的过期时间,tomcat的默认过期时间是 30分钟

100. 利用线程池的优势:

1降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

2、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

3、提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

boolean 单独使用的时候还会被变成int,4个字节

boolean[] 数组使用的时候变成byte,1个字节

101. CacheBuffer的区别

Cache:缓存区,位于cpu和主内存之间容量很小但速度很快的存储器,因为CPU的速度远远高于主内存的速度,CPU从内存中读取数据许要等待很长的时间,而Cache保存着CPU刚用过的数据或循环使用的部分数据,Cache读取数据更快,减少cpu等待时间。

Buffer:缓冲区,用于存储速度不同步的情况,处理系统两端速度平衡,为了减小短期内突发I/O的影响,起到流量整形的作用。速度慢的可以先把数据放到buffer,然后达到一定程度在读取数据

内连接:

必须两个表互相匹配才会出现

外链接

左外链接:左边不加限制

右外连接:右边不加限制

全连接:左右都不加限制

102. 如何创建索引

全文检索的索引创建过程一般有以下几步:

第一步:一些要索引的原文档(Document)

为了方便说明索引创建过程,这里特意用两个文件为例:

文件一:Students should be allowed to go out with their friends, but not allowed to drink beer.

文件二:My friend Jerry went to school to see his students but found them drunk which is not allowed.

第二步:将原文档传给分词器(Tokenizer)

分词器(Tokenizer)会做以下几件事情(此过程称为Tokenize)

1. 将文档分成一个一个单独的单词。

2. 去除标点符号。

3. 去除停词(Stop word)

所谓停词(Stop word)就是一种语言中最普通的一些单词,由于没有特别的意义,因而大多数情况下不能成为搜索的关键词,因而创建索引时,这种词会被去掉而减少索引的大小。

英语中停词(Stop word)如:“the”,“a”,“this”等。

对于每一种语言的分词组件(Tokenizer),都有一个停词(stop word)集合。

经过分词(Tokenizer)后得到的结果称为词元(Token)

在我们的例子中,便得到以下词元(Token):

“Students”,“allowed”,“go”,“their”,“friends”,“allowed”,“drink”,“beer”,“My”,“friend”,“Jerry”,“went”,“school”,“see”,“his”,“students”,“found”,“them”,“drunk”,“allowed”。

第三步:将得到的词元(Token)传给语言处理组件(Linguistic Processor)

语言处理组件(linguistic processor)主要是对得到的词元(Token)做一些同语言相关的处理。

对于英语,语言处理组件(Linguistic Processor)一般做以下几点:

1. 变为小写(Lowercase)

2. 将单词缩减为词根形式,如“cars”“car”等。这种操作称为:stemming

3. 将单词转变为词根形式,如“drove”“drive”等。这种操作称为:lemmatization

而且在此过程中,我们惊喜地发现,搜索“drive”,“driving”,“drove”,“driven”也能够被搜到。因为在我们的索引 中,“driving”,“drove”,“driven”都会经过语言处理而变成“drive”,在搜索时,如果您输入“driving”,输入的查询 语句同样经过我们这里的一到三步,从而变为查询“drive”,从而可以搜索到想要的文档。

ZK实现分布式锁- 是否存在,并且最小的

根据ZK中节点是否存在,可以作为分布式锁的锁状态,以此来实现一个分布式锁,下面是分布式锁的基本逻辑:

客户端调用create()方法创建名为“/dlm-locks/lockname/lock-”的临时顺序节点。

客户端调用getChildren(“lockname”)方法来获取所有已经创建的子节点。

客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,那么就认为这个客户端获得了锁。

如果创建的节点不是所有节点中需要最小的,那么则监视比自己创建节点的序列号小的最大的节点,进入等待。直到下次监视的子节点变更的时候,再进行子节点的获取,判断是否获取锁。

而且zk的临时节点可以直接避免网络断开或主机宕机,锁状态无法清除的问题,顺序节点可以避免惊群效应。这些特性都使得利用ZK实现分布式锁成为了最普遍的方案之一。

Redus实现分布式锁,使用setNX (set if not exists)

getset(先写新值返回旧值,用于分辨是不是首次操作) 防止网络断开后 会设置超时

http://blog.csdn.net/ugg/article/details/41894947

SETNX 可以直接加锁操作,比如说对某个关键词foo加锁,客户端可以尝试

SETNX foo.lock

如果返回1,表示客户端已经获取锁,可以往下操作,操作完成后,通过

DEL foo.lock

命令来释放锁。

处理死锁

在上面的处理方式中,如果获取锁的客户端端执行时间过长,进程被kill掉,或者因为其他异常崩溃,导致无法释放锁,就会造成死锁。所以,需要对加锁要做时 效性检测。因此,我们在加锁时,把当前时间戳作为value存入此锁中,通过当前时间戳和Redis中的时间戳进行对比,如果超过一定差值,认为锁已经时 效,防止锁无限期的锁下去,但是,在大并发情况,如果同时检测锁失效,并简单粗暴的删除死锁,再通过SETNX上锁,可能会导致竞争条件的产生,即多个客 户端同时获取锁。

C1获取锁,并崩溃。C2和C3调用SETNX上锁返回0后,获得foo.lock的时间戳,通过比对时间戳,发现锁超时。

C2 向foo.lock发送DEL命令。

C2 向foo.lock发送SETNX获取锁。

C3 向foo.lock发送DEL命令,此时C3发送DEL时,其实DEL掉的是C2的锁。

C3 向foo.lock发送SETNX获取锁。

此时C2和C3都获取了锁,产生竞争条件,如果在更高并发的情况,可能会有更多客户端获取锁。所以,DEL锁的操作,不能直接使用在锁超时的情况下,幸好我们有GETSET方法,假设我们现在有另外一个客户端C4,看看如何使用GETSET方式,避免这种情况产生。

C1获取锁,并崩溃。C2和C3调用SETNX上锁返回0后,调用GET命令获得foo.lock的时间戳T1,通过比对时间戳,发现锁超时。

C4 向foo.lock发送GESET命令,

GETSET foo.lock

并得到foo.lock中老的时间戳T2

如果T1=T2,说明C4获得时间戳。

如果T1!=T2,说明C4之前有另外一个客户端C5通过调用GETSET方式获取了时间戳,C4未获得锁。只能sleep下,进入下次循环中。

现在唯一的问题是,C4设置foo.lock的新时间戳,是否会对锁产生影响。其实我们可以看到C4和C5执行的时间差值极小,并且写入foo.lock中的都是有效时间错,所以对锁并没有影响。

为了让这个锁更加强壮,获取锁的客户端,应该在调用关键业务时,再次调用GET方法获取T1,和写入的T0时间戳进行对比,以免锁因其他情况被执行DEL 外解开而不知。以上步骤和情况,很容易从其他参考资料中看到。客户端处理和失败的情况非常复杂,不仅仅是崩溃这么简单,还可能是客户端因为某些操作被阻塞 了相当长时间,紧接着 DEL 命令被尝试执行(但这时锁却在另外的客户端手上)。也可能因为处理不当,导致死锁。还有可能因为sleep设置不合理,导致Redis在大并发下被压垮。 最为常见的问题还有

 AOF重写带有子进程副本保证安全

103. Java虚拟机Java内存结构,分区,每个区放置什么

程序计数器:(线程私有)当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码的指令,以程序中分支、循环和跳转等流程的控制都离不开这个计数器的指示。

虚拟机栈:(线程私有),每个方法在执行时都会创建一个栈桢,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。一个方法从调用到执行完成的过程,对应的栈桢在虚拟机栈的进出过程。当线程结束时,虚拟机栈中的数据会被自动的释放。

局部变量表:基本数据类型、对象的引用、返回地址,局部变量表锁需要的内存空间是在程序编译时就已经会被确定好的。

本地方法栈:(线程私有)虚拟机栈是为执行java方法所服务的,而本地方法栈是为了虚拟机使用到的本地方法锁服务的。

堆区:(线程共享)java堆是被所有的线程所共享的一片区域,所有的对象的实例和数组都会在堆区尽心分配。java堆细分:新生代和老年代;也可能会划分出多个线程锁共享额分配缓冲区TLAB;

Java堆可以在物理上不连续的内存空间中,只要逻辑上连续就可以。

方法区:(线程共享)存储已经被虚拟机加载过的类的信息、常量、静态变量和及时编译器编译后的代码。在方法区中一个区域叫做:运行时常量池,用于存放编译后生成的字面量和符号的引用。

堆的分代

(1)年轻代:

所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。

大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当一个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当另一个Survivor区也满了的时候,从前一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。

(2)年老代:

在年轻代中经历了N(可配置)次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

(3)持久代:

用于存放静态数据,如 Java Class, Method 等。持久代对垃圾回收没有显著影响。

104. OOM异常的处理思路

对象的创建方法,对象的内存的分配,对象的访问定位

对象的创建:

(1)第一步,当遇到一个new的指令,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,需要先执行相应的类加载过程;

(2)第二步,根据类加载完成后确定的内存大小,为对象分配内存;

(3)第三步,需要对分配到的内存空间都初始化为零值;

(4)第四步,虚拟机要对对象设置一些基本信息,如对象是那个类的实例、对象的哈希码、对象的GC分代年龄信息、如何才能找到类的元数据信息等,到这里虚拟机创建对象的工作已经完成;

(5)第五步,从程序的角度,我们还需要对对象进行初始化操作。

对象的内存分配:

(1)对象头:

存储hashcode 、gc分代年龄以及一些必要的自身的运行时数据

(2)实例数据:

存储真实的数据信息

(3)对齐填充:

仅仅起到占位符的作用

对象的访问定位:

通过句柄池的访问,在句柄池中保存着到对象实例数据的指针以及到对象类型的数据的指针

通过直接的指针服访问,通过引用直接指向java堆的对象的实例数据

105. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?

标记清除法:

就是先标记哪些对象实例不用,然后直接清除。缺点就是产生大量的内存碎片,下次若要存储一个大的对象,无法找到连续内存而又必须提前GC

标记整理:

也就是先标记,然后对存活的对象进行移动,全部移动到一端,然后再对其它的内存进行清理。

复制算法:

把内存分成相等的AB两块,每次只使用其中的一块。比如当A内存使用完后,就把A中还存活着的对象复制到另外一块内存中去(B),然后再把已经使用过的内存清理掉。优点:这样就不用考虑内存碎片的问题了。缺点:内存减半,代价略高。

106. GC收集器有哪些?CMS收集器与G1收集器的特点。

对于新生代的收集器:

Serial单线程收集器 parnew多线程收集器  parallelSccavenge收集器

对于老年代的收集器:

CMS并发收集低停顿收集器  serial Old单线程收集器  parallel Old多线程收集器

107. CMS收集器:

优点:并发收集、低停顿

缺点:

(1)对cpu资源非常的敏感,在并发的阶段虽然不会导致用户的线程停顿,但是会由于占用一部分的线程导致应用程序变慢,总的吞吐量会降低;

(2)无法去处理浮动垃圾;

(3)基于“标记-清除”算法的收集器,所以会出现碎片。

114G1收集器:

优点:

(1)能充分利用cpu、多核的优势,使用多个cpu缩短停顿的时间;

(2)分代收集,不要其他收集器的配合便可以独立管理整个的GC堆;

(3)空间整合:整体基于“标记-清理”算法的实现,局部是基于“复制”算法的实现;

(4)可以预测的停顿

108. Minor GCFull GC分别在什么时候发生?

Minor GC:新生代GC,当jvm无法为一个新的对象分配空间时会触发

Full GC:整个堆空间的GC

109. 类加载的五个过程:加载、连接、初始化。

类的加载:将类的class文件读入内存,并创建一个叫做java.lang.Class对象,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。

这些类的class文件的来源:

(1)从本地文件系统中加载class文件

(2)从jar包中加载class文件,比如jdbc编程时

(3)通过网络加载class文件

(4)把一个java源文件动态编译,并执行加载

连接:

(1)验证:验证阶段用于检验被加载的类是否具有正确的内部结构,并和其他的类协调一致

(2)准备:为类的类变量分配内存,并去设置默认的值

(3)解析:将类的二进制数据中的符号引用替换成直接引用。

初始化:

主要是对类变量进行初始化。

(1)如果该类还没有被加载和连接,则先进行加载连接

(2)如果该类的直接父类还没有被初始化,则先初始化其直接父类

(3)类中如果有初始化的语句则先去执行这些初始化语句。

110. 反射

概念:在运行的状态中,对于任何一个类或者对象,可以知道其任意的方法和属性,这种动态地调用其属性和方法的手段叫做反射。利用的反编译的手段

一、通过三种方式来获取Employee类型,获取类:

(1)Class c1 = Class.forName(“Employee”);

(2)Class c2 =Employee.class;

(3)Employee e = new Employee(); Class c3 = e.getClass();

二、得到class的实例:

Object o = c1.newInstance();

三、获取所有的属性

Field[] fs = c.getDeclaredFields();  

四、获取所有的方法

GetDeclareMethods();

动态代理

J2se多线程(线程锁)

线程的状态:

新建状态、就绪状态、运行状态、阻塞状态、死亡状态(线程状态转换图)

多线程的创建和启动:

(1)继承Thread类,重写类的run方法,调用对象的start方法启动

(2)实现Runnable接口,并重写该接口的run方法,该方法同样是线程的执行体,创建runnable实现类的实例,并以此实例作为Thread类的target来创建thread对象,该thread对象才是真的线程对象。

(3)使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。

方法3:

// 创建MyCallable对象

Callable myCallable = new MyCallable();

//使用FutureTask来包装MyCallable对象

FutureTask ft = new FutureTask(myCallable);

//FutureTask对象作为Thread对象的target

Thread thread = new Thread(ft);

//线程进入到就绪状态

thread.start();   

线程同步的方法:sychronizedlockreentrantLock

synchronized修饰同步监视器:修饰可能被并发访问的共享资源充当同步监视器;

synchronized修饰方法,同步方法的同步监视器是this,也就是调用该方法的对象;

synchronizedd可以用来修饰方法,可以修饰代码块,但是不能修饰构造器和成员变量;

使用lock锁对象,每次只能有一个线程对lock对象进行加锁和释放锁,线程开始访问该锁对象时必须先获得锁lock

基本用法:

Private final ReentrantLock lock = new ReentrantLock();

Lock.lock();

Try(){

}catch(Exception e){

}finally{}

Lock.unlock();

锁的等级:内置锁、对象锁、类锁、方法锁。

内置锁:每一个java对象都可以用做一个实现同步的锁,这个锁成为内置锁。当一个线程进入同步代码块或者方法的时候会自动获得该锁,在退出同步代码块或者方法时会释放该锁。

获得内置锁的方法:进入这个锁的保护的同步代码块或者方法

注意:java内置锁是一个互斥锁,最多只有一个线程能够获得该锁。

对象锁:对象锁是用于对象实例方法,或者一个对象实例上的。

类锁:类锁用于类的静态方法或者一个类的class对象上,一个类的对象实例有多个,但是每个类只有一个class对象,即不同对象实例的对象锁是互不干扰的,每一个类都有一个类锁。类锁只是概念上的,并不是真实存在的。

方法锁:synchronized修饰方法,同步方法的同步监视器是this,也就是调用该方法的对象;

111. ThreadLocal的设计理念与作用。

作用:

ThreadLocal类只能去创建一个被线程访问的变量,如果一段代码含有一个ThreadLocal变量的引用,即使两个线程同时执行这段代码,它们也无法访问到对方的ThreadLocal变量。

创建ThreadLocal的方式:

private ThreadLocal myThreadLocal = new ThreadLocal();

我们可以看到,通过这段代码实例化了一个ThreadLocal对象。

我们只需要实例化对象一次,并且也不需要知道它是被哪个线程实例化。

虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程却只能访问到自己通过调用ThreadLocal的set()方法设置的值。即使是两个不同的线程在同一个ThreadLocal对象上设置了不同的值,他们仍然无法访问到对方的值。

如何为ThreadLocal对象赋值和取值:

一旦创建了一个ThreadLocal变量,你可以通过如下代码设置某个需要保存的值:

myThreadLocal.set("A thread local value”);

可以通过下面方法读取保存在ThreadLocal变量中的值:

String threadLocalValue = (String) myThreadLocal.get();

get()方法返回一个Object对象,set()对象需要传入一个Object类型的参数。

初始化该ThreadLocal变量:

通过创建一个ThreadLocal的子类重写initialValue()方法,来为一个ThreadLocal对象指定一个初始值。

112. ThreadPool用法与优势。

优势:

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。           

用法:

线程池的创建:

new  ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);

参数:corePoolSize:线程池的基本大小

maximumPoolSize:线程池的最大大小

runnableTaskQueue:任务队列

keepAliveTime:线程活动保持时间

执行方式:

threadsPool.execute(handler);

threadsPool.submit(handler);

线程池的关闭:

Shutdown和shutdownNow方法实现

线程池的工作流程分析:

先将任务提交的顺序为核心线程池、队列、线程池、当这三个关节都不能执行用户所提交的线程时,则抛出“无法执行的任务”。     

113. 字节流和字符流

(1)java中字节流处理的最基本的单位是单个字节。通常用来处理二进制数据,最基本的两个字节流类是InputStream和OutputStream,这两个类都为抽象类。

字节流在默认的情况下是不支持缓存的。每次调用一次read方法都会请求操作系统来读取一个字节,往往会伴随一次磁盘的IO,如果要使用内存提高读取的效率,应该使用BufferedInputStream。

(2)字符流处理的最基本的单元是unicode(码元),通常用来来处理文本数据。

输入字符流(文件到内存):把要读取的字节序列按照指定的编码方式解码为相应的字符序列,从而可以存在内存中。

输出字符流(内存到文件):把要写入文件的字符序列转为指定的编码方式下的字节序列,然后写入文件中。

区别如下:

1、字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。

unicode的编码范围:0x0000~0XFFFF,在这个范围的每个数字都有一个字符与之对应

2、字节流默认不使用缓冲区;字符流使用缓冲区。

3、字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。

114. 序列化(常见的序列化操作)

含义:

java序列化:将java对象转换为字节序列的过程;

java反序列化:将字节序列恢复为java对象的过程

序列化的目的:

实现数据的持久化,将数据永久地保存在磁盘上,通常放在文件中;

利用序列化实现远程的通讯,在网络上传送对象的字节序列.

实现序列化的三种方法:

1)某实体类仅仅实现了serializable接口(常用)

序列化步骤:

步骤一:创建一个对象输出流,它可以包装一个其它类型的目标输出流,如文件输出流:

ObjectOutputStream out = new ObjectOutputStream(new fileOutputStream(“D:\\objectfile.obj”));

步骤二:通过对象输出流的writeObject()方法写对象:

//Hello对象的字节流将输入到文件

out.writeObject(“Hello”);

反序列化步骤:

步骤一:创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:

ObjectInputStream in = new ObjectInputStream(new fileInputStream(“D:\\objectfile.obj”));

步骤二:通过对象输出流的readObject()方法读取对象:

//将从文件中读取到字节序列转化为对象

String obj1 = (String)in.readObject();

2)若实体类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。

ObjectOutputStream调用该对象的writeObject(ObjectOutputStream out)的方法进行序列化。

ObjectInputStream会调用该对象的readObject(ObjectInputStream in)的方法进行反序列化。

3)若Student类实现了Externalnalizable接口,且Student类必须实现readExternal(ObjectInput in)writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。

ObjectOutputStream调用Student对象的writeExternal(ObjectOutput out))的方法进行序列化。

ObjectInputStream会调用Student对象的readExternal(ObjectInput in)的方法进行反序列化。

115. String,StringBuffer,StringBuilder的区别,应用场景

1)在执行的速度上:StringBuilder>StringBuffer>String

2)String是字符串常量 StringBuffer和StringBuilder是字符串变量

例子1:

String s = “abcd”;

s=s+1;

Syos(s);

底层执行:首先创建一个对象s,赋予abcd.然后又创建新的对象s,之前的对象并没有发生变化,利用string操作字符串时,是在不断创建新的对象,而原来的对象由于没有了引用,会被GC,这样执行的效率会很低。

例子2:

String str2 = “This is only a”;

String str3 = “ simple”;

String str4 = “ test”;

String str1 = str2 +str3 + str4;

同理:str2 str3 str3没有被引用,但是创建了新的对象str1,执行速度上会很慢。

StringBuilder:线程非安全的

StringBuffer:线程安全的

例子3:

StringBuffer builder = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);

应用场景:

A:使用要操作少量的数据时,使用String

B:单线程操作字符串缓冲区下操作大量数据使用StringBulider

C:多线程操作字符串缓冲区下操作大量的数据使用StringBuffer

116. HashMapHashTable的区别

HashMap是线程不安全的;允许有null的键和值;执行的效率高一点;方法不是synchronize的要提供外同步;包含有containsvalue和containskey的方法

HashTable是线程安全的;不允许有null的键和值;效率稍微低些;方法是synchronize的;包含contains方法

117. GC垃圾回收机制原理

(参看博客)

118. ==与 equals区别

==:对于基本数据类型的变量,直接比较存储的值是否相等;作用于引用类型的变量,则比较的是该变量所指向的地址是否相同。

equals:不同作用于基本数据类型的变量,如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址(相当于直接使用父类的equals方法,而该方法则是用==进行的比较,所以结果和用==比较的效果是一样的);但是比如String Date类对equals进行了重写,比较的是字面量。

119. final关键字

对于基本的数据类型,使用final关键字将使得数值恒定不变;

对于对象引用,final则是引用恒定不变,一但被初始化指向一个对象,它就不会再指向另外一个对象,但是该对象本身是可以被修改的;

对于类,如果不想继承某个类,可以将该类设置为fianl形式,该类不会有子类;

对于方法,final修饰的方法不会被重写

对于空白的final,对于没有给定初始值的fianl,编译器会在使用前初始化该final修饰的变量

对于宏变量:被final修饰的变量为宏常量 在编译的阶段被其本身的值直接替换

short s1=1s1 s1+1

表达式类型的自动提升,一个short类型的变量和一个int型的数在一起进行运算,会将short类型的数隐式转换为int参与运算,但是该运算的结果为int类型是不同直接赋值给一个short类型的,必须进行强制的类型转换,否则编译是通不过的。

120. 八种基本数据类型的大小,以及他们的封装类。

类型转换:byte (1字节)--->short(1)/char(2)--->int(4)--->long(8)--->float(4)--->double(8)

分装类:Byte Short Character Integer Long Float Double

121. Switch能否用string做参数?(分版本讨论)

(1)在jdk1.7版本前不支持string作为参数,仅仅支持byte、short、char,因为可以转换为int,但是long和string不能转换为int,所以不能使用。

(2)在jdk1.7之后,支持使用string作为case的参数,实际匹配的是该字符串的hash值,然后用equals进行安全性检查。Switch支持String其实是一个语法糖,在编译后的字节码文件中都会被还原成原生的类型,并在相应的位置插入强制转换的代码,底层的JVM在switch上并没有修改;当传入switch是null时,在运行时对一个null调用hashcode()方法,会抛出空指针异常。

122. Object有哪些公用方法?

object是所有类的父类,任何类都默认继承Object类

9、Clone

private保护方法,实现对象的浅复制,只有类实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportException

10、Equals

在object中与==是一样的,子类一般需要重写该方法

11、hashCode

该方法用于哈希查找,重写了equals方法一般都要重写hashcode方法,这个方法在一些具有哈希功能的collection中使用

12、getClass

final方法,获得运行时的类型

13、wait方法

使得当前的线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。Wait方法会一直等待,直到获得锁(到了睡眠的时间间隔也会唤醒自己)或者被中断掉。

调用该方法,当前的线程会进入到睡眠的状态,直到调用该对象的notify方法、notifyAll方法、调用interrupt中断该线程,时间间隔到了。

14、Notify

唤醒在该对象上的等待的某个线程

15、notifyAll

唤醒在该对象上的等待到所有的线程

16、toString

把对象转换成string类型进行输出

123. Java的四种引用,强弱软虚,用到的场景。

引用的级别:

强引用>软引用>弱引用>虚引用

强引用:如果一个对象具有强引用,垃圾回收器绝对不会回收它。当内存空间不足时,jvm宁愿抛出outofmemoryError,使得程序的异常终止。

软引用:如果一个对象具有软引用,则内存空间足够,垃圾回收机制就不会去回收它,当内存不足时,就会进行回收。如果软引用所引用的对象被垃圾回收器回收,java虚拟机就会把这个软引用加入到与之关联的引用队列。

应用场景:实现内存敏感的高速缓存

弱引用:在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

应用场景:gc运行后终止

虚引用:就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

124. 写出生产者消费者模式。

125. Hashcode的作用。

(1)Hashcode 的存在主要用于解决查找的快捷性,比如在hashmap、hashtable中,hashcode是用来在散列的存储结构中确定对象的存储的位置的。

(2)如果两个对象相同,就是通过equals方法比较返回true,两个对象装在一个桶里,也就是hashcode也要一定相同。

(3)两个对象的hashcode相同,并不一定代表两个对象就是相同的,只能说明他们存储在一个桶里。

(4)一般重写了equals方法,也尽量去重写hashcode方法,保证先找到该桶,再去找到对应的类,通过equals方法进行比较。

126. ArrayListLinkedListVector的区别。

1ArrayList:是基于动态数组的数据结构,LinkedList的基于链表的数据结构

2、对于随机访问get和set,ArrayList性能较好,因为LinkedList会去移动指针

3、对于新增和删除的操作,linkedList只需要修改指针的指向,性能较好,但是arrayList会移动数据。

vector的特点:

1、vector的方法都是线程同步的,是线程安全的,但是arraylist和linkedlist不是,由于线程的同步必然会影响性能,所以vector的性能不太高。

2、当vector或者arraylist的元素超过它的初始的大小时,vector会将容量翻倍,但是arraylist只会增加50%,这样有利于节约内存的空间。

127. MapSetListQueueStack的特点与用法。

128. HashMapConcurrentHashMap的区别

hashMap不是线程安全的;

concurrentHashMap是线程安全的;在其中引入了“分段锁”,而不是将所有的方法加上synchronized,因为那样就变成了hashtable.

所谓“分段锁”,就是把一个大的Map拆分成N个小的hashtable,根据key.hashcode()决定把key放在哪一个hashtable中。

通过把整个Map分为N个Segment(类似HashTable),可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。

129. TreeMapHashMapLindedHashMap的区别。

HashMap:根据键的hashcode值进行存储数据,根据键可以直接获取它的值,具有快速访问的特点,遍历时取得数据是随机的,hashmap最多只允许一条记录的键为null(set无序不重复),允许多条记录的值为Null;如果要保证线程的同步,应该使用Collections.synchronizedMap()方法进行包装,或者使用ConcurrentHashMap

LinkedHashMap:保存了记录的插入的顺序,在迭代遍历Linkedhashmap时,先得到的记录肯定是先插入的,它遍历的速度只和实际的数据有关和容量没关。

TreeMap:实现的是SortMap,能够把保存的记录按照键进行排序,默认会按照键值的升序进行排序,当遍历TreeMap时得到的记录是排序过后的。

Collection包结构,与Collections的区别。

Collection是一个集合的接口,提供了对集合对象进行操作的通用的方法。

在它下面的子接口:set、list、map

java.util.Collections是一个包装的类,包含有各种的有关集合操作的静态方法,比如包含对集合的搜索、排序、线程安全化等一系列的操作,此类不能被实例化,相当于是操作集合的工具类,服务于java的collection的框架。

介绍下Concurrent

concurrent包基本有3个package组成

(1)java.util.concurrent:提供大部分关于并发的接口和类,如BlockingQueue,Callable,ConcurrentHashMap,ExecutorService, Semaphore等

 

(2)java.util.concurrent.atomic:提供所有原子操作的类,如AtomicInteger, AtomicLong等;

 

(3)java.util.concurrent.locks:提供锁相关的类, 如Lock, ReentrantLock, ReadWriteLock, Condition等;

concurrent包的优点:

1. 首先,功能非常丰富,诸如线程池(ThreadPoolExecutor),CountDownLatch等并发编程中需要的类已经有现成的实现,不需要自己去实现一套;毕竟jdk1.4对多线程编程的主要支持几乎就只有Thread, Runnable,synchronized等

2. concurrent包里面的一些操作是基于硬件级别的CAS(compare and swap),就是在cpu级别提供了原子操作,简单的说就可以提供无阻塞、无锁定的算法;而现代cpu大部分都是支持这样的算法的;

Try-catch -finallytry里有returnfinally还执行么?

任然会执行。

1、不管有木有出现异常,finally块中代码都会执行;

 

2、当try和catch中有return时,finally仍然会执行;

 

3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;

 

4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

130. ExcptionError包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。

131. Java面向对象的三个特征与含义。

封装:

是指将某事物的属性和行为包装到对象中,这个对象只对外公布需要公开的属性和行为,而这个公布也是可以有选择性的公布给其它对象。在Java中能使用private、protected、public三种修饰符或不用(即默认defalut)对外部对象访问该对象的属性和行为进行限制。

继承:

是子对象可以继承父对象的属性和行为,亦即父对象拥有的属性和行为,其子对象也就拥有了这些属性和行为。

多态:

java的引用变量有两种类型,一个是编译时的类型,一个是运行时的类型,编译时类型由申明该变量时的类型决定,运行时的类型由实际赋值给该变量的对象所决定,如果编译时的类型和运行时的类型不一致就可能出现所谓的多态。

在java中把一个子类的对象直接赋值给一个父类的引用变量,当运行该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,这就有可能出现,相同类型的变量,调用同一个方法时呈现多种不同的行为特征,出现了“多态”

132. OverrideOverload的含义和区别。

Overload:方法重载,在同一个类中,方法名相同,参数列表不同,至于方法的修饰符,反回值的类型,与方法的重载没有任何的联系。

Override:方法重写,两同两小一大

两同:方法名称相同、参数列表相同

两小:返回值类型要小或者相等;抛出的异常要小或者相等

一大:子类方法的访问权限要相等或者更大

133. Interfaceabstract类的区别。

实例化:

都不能被实例化

类:一个类只能继承一次abstract类;一个类可以实现多个interface

数据成员:可以有自己的;接口的数据成员必须定义成static final的

方法:可以有私有的,非abstract方法必须实现;接口中不可以有私有的方法,默认都是public abstract的

变量:可以有私有的,其值可以在子类中重新定义,也可以重新赋值;接口中不可以有私有的成员变量,默认是public static final 实现类中不能去重新定义和改变其值

134. Java IONIO

IO是面向流的,NIO是面向缓冲区

Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

阻塞与非阻塞IO

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

选择器(Selectors)

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

反射的作用原理。

见博客

135. wait()sleep()的区别。

(1)sleep方法,该方法属于thread类;wait方法属于object类

(2)sleep方法导致程序会暂停执行指定的时间,让出cpu给其他的线程,但是他还是监控状态的保持者,当到达指定的时间又会自动恢复运行。也就是调用sleep方法线程不会释放对象锁;调用wait方法会释放对象锁,进入到等待此对象的等待锁定池,只有当针对此对象调用了notify()方法后,才会获取对象锁进入运行的状态。

136. foreach与正常for循环效率对比。

For循环可以从前向后遍历,也可以从后向前遍历,可以不逐个遍历,通常用于已知次数的循环。

foreach循环不能向迭代变量赋值,通常对集合对象从头到位进行读取,其有优化的存在。

137. JavaC++对比。

1、指针

java语言不提供指针,增加了自动的内存管理,有效的防止c/c++中的指针操作失误。

2、多重继承

C++支持多重继承,java不支持多重继承,但是允许实现多个接口。

3、数据类型和类

java将数据和方法结合起来,分装到类中,每个对象都可以实现自己的特点和方法;而c++允许将函数和变量定义全局的。

4、内存管理

java可以对所有的对象进行内存管理,自动回收不再使用的对象的内存;c++必须由程序员显式分配内存释放内存。

5、操作符的重载

C++支持操作符的重载,java不允许进行操作符的重载。

6、预处理功能

java不支持预处理功能,c++有一个预编译的阶段,也就是预处理器。

7、字符串

C++不支持字符串,java中支持字符串,是java的类对象。

8、数组

java引入了真正的数组,不同于c++中利用指针实现的伪数组。

9、类型的转换

C++中有时会出现数据类型的隐含转换,设计到自动强制类型的转换问题,比如存在将浮点数直接转换为整数的情况,java不支持自动的强制类型转换,如果需要,必须显示进行强制的类型转换。

10、异常

java中使用try{}catch(){}finally{}进行异常的处理,c++没有。

138. HTTPHTTPS的区别

https:是http的安全版本,利用ssl可以对所传输的数据进行加密,默认端口是443

139. cookiesession的区别

(1)cookie数据存放在客户的浏览器上,session数据放在服务器上。 

(2)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session 

(3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE 

(4)单个cookie在客户端的限制是4K,就是说一个站点在客户端存放的COOKIE不能4K。

(5)所以:将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中

140. 网路TCP三次握手、四次挥手,各个状态的名称和含义timewait的作用?

ACK:tcp协议规定,只有ack=1时才有效,在连接建立后所有发送的豹纹的ack=1

Syn(SYNchronization):在连接建立时用来同步序号。

当SYN=1而ACK=0:这是一个连接请求报文;

当对方同意建立连接时,则应该使得SYN=1而且ACK=1;

当SYN=1:这是一个连接请求或者连接接受报文

FIN(finis):终结的意思,用来释放一个连接。当fin=1,表示次报文段的发送方的数据已经发送完毕并要求释放连接。

A的状态:关闭状态--->同步已发送--->已建立

B的状态:关闭状态--->监听状态--->同步收到--->已建立

A:建立状态--->终止等待1--->终止等待2--->等待2MSL

B:建立状态--->关闭等待--->最后确认

Timewait的作用?

(1)为了保证A发送最后一个ACK报文能到达B,因为这个ACK报文有可能会丢失,这样会使得处在最后确认阶段的B收不到已经发送的FIN+ACK的确认信息,B会超时重传该报文段,在2MSL的时间内,A会收到信息,重传依次确认,重启该计时器。

(2)保证在2MSL的时间内,所有在本网络上产生的报文段都消失,使得在新的连接中不会出现旧的连接请求的报文段。

2)SYN攻击防范

141. TCP/IP层次架构,每层的作用和协议

OSI模型:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

TCP/IP模型:应用层、传输层、网络互联层、主机到网络层

协议:

(1)应用层:FTP、TELNET、HTTP | SNMP、TFTP、NTP

将OSI模型的会话层和表示层整合成应用层,应用层面向不同的网络应用引入了不同的应用层协议。

(2)传输层:TCP|UDP

功能是使得源端主机和目标端主机上的对等实体可以进行会话,定义了两种服务质量不同的协议,分别是TCP和UDP协议。

TCP协议是一个面向连接的、可靠的协议。它将一台主机发出的字节流无差错地发往互联网上的其他主机。在发送端,它负责把上层传送下来的字节流分成报文段并传递给下层。在接收端,它负责把收到的报文进行重组后递交给上层。TCP协议还要处理端到端的流量控制,以避免缓慢接收的接收方没有足够的缓冲区接收发送方发送的大量数据。  

UDP协议是一个不可靠的、无连接协议。主要适用于不需要对报文进行排序和流量控制的场合。

(3)网络互联层:IP

网络互联层是整个TCP/IP协议栈的核心。功能是把分组发往目标网络或者主机。为了尽快发送分组,可能会沿着不同的路径同时进行分组传递。因此,分组到达的顺序和发送的顺序可能会不一致,这就需要上层必须对分组进行排序。同时它可以将不同类型的网络进行互联,完成拥塞控制的功能。

(4)主机到网络层:以太网、令牌环网、PPP

该层未被定义,具体的实现方式随着网络类型的不同而不同。

142. TCP拥塞控制

拥塞:计算机网络中的带宽、交换节点中的缓存和处理机都是网络中的资源,当在某一个时间,对网络中的某一个资源的需求超出了该资源所能提供的部分,网络的性能会变坏,就出现了拥塞。

拥塞控制:防止过多的数据注入到网路,使得网络中的路由器和链路不至于过载。拥塞控制是一个全局的过程,和流量控制不同,流量控制是点对点的通信量的控制。

慢开始和拥塞避免:

发送方维持一个叫做拥塞窗口的状态变量,拥塞窗口取决于网络的拥塞程度,并且会动态的变化。发送方让自己的发送窗口等于拥塞窗口,考虑接受方的接受能力,发送窗口可能会小于拥塞窗口。

慢开始算法:不要一开始就发送大量的数据,先探测下网络的拥塞程度,由小到大逐渐增加拥塞窗口的数量。

拥塞避免算法:让拥塞窗口缓慢增长,每进过一个往返时间就把发送方的拥塞窗口cwnd+1,而不是加倍,此时拥塞窗口按照线性的规律缓慢增长。

结合使用:为了防止拥塞窗口增长过大引发网络的拥塞,设置一个慢开始门限ssthresh状态变量。其用法:

当cwnd使用慢开始算法

当cwnd>ssthresh,使用拥塞避免算法

当cwnd=ssthresh,慢开始算法和拥塞避免算法随意。

当遇到网络拥塞时,就把慢开始门限设置为出现拥塞时发送窗口大小的一半,同时将拥塞的窗口设置为1,再重新开始执行慢开始算法。

143. 滑动窗口是什么设计的?

窗口:是一段可以被发送者发送的字节序列,其连续的范围称为“窗口”

滑动:这段“允许发送的范围”是随着发送的过程而不断变换的,表现的形式就是“按照顺序滑动”

流量控制:

(1)TCP利用滑动窗口实现流量的控制机制

(2)如何考虑流量控制中的传输效率

流量控制,接受方传递信息给发送方,使其发送数据不要太快,是一种端到端的控制,主要的方式是返回的ack中会包含自己的接受的窗口的大小,发送方收到该窗口的大小时会控制自己的数据发送。

传递效率:单个发送字节单个确认,和窗口有一个空余即通知发送方发送一个字节,会增加网络中许多不必要的报文,因为会为一个字节数据添加40个字节的头部。

144. 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则是不可靠

145. TCP报文结构

紧急比特URG:URG=1,注解该报文应该尽快送达,而不需要按照原来的的队列次序依次送达

确认比特ACK:只有当ACK=1,确认序号字段才有意义

急迫比特PSH:当PSH=1时,注解恳求远地TCP将本报文段立即传送给应用层

复位比特RST:当注解呈现严重错误时,必须开释连接,进行重新的传输连接

同步比特SYN:当SYN=1而ACK=0时,这是一个连接请求报文段,若对方赞成连接请求会将SYN=1而且ACK=1

终止比特FIN:当FIN=1,注解字符串已经发送完毕,并请求开释传输连接。

146. HTTP的报文结构(请求报文+响应报文)

HTTP请求报文:(1)请求行+(2)请求头部+(3)请求正文

(1)请求行:请求方法+URL+协议版本

请求方法:常用GET、POST

协议版本:HTTP/主版本号.次版本号  常用HTTP/1.0和HTTP/1.1

(2)为请求报文添加的一些附加的信息,“名/”组成,并且是每行一对 用冒号进行分割

在请求头部存在空行,表示请求头部的结束,接下来是请求正文!

区别get和post方式

对于get方式没有请求的正文,对于post方式有请求的正文。

HTTP响应的报文格式:

(1)状态行+(2)响应头部+(3)响应正文

(1)状态行:协议版本+状态码+状态码描述

(2)响应头部:也是由键值对所组成

(3)响应正文,由服务器端接受数据

147. HTTP状态码的含义

148. http request的几种类型(8种)

(1)OPTIONS:返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。

(2)HEAD:向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。

(3)GET:向特定的资源发出请求。

(4)POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。

(5)PUT:向指定资源位置上传其最新内容。

(6)DELETE:请求服务器删除Request-URI所标识的资源。

(7)TRACE:回显服务器收到的请求,主要用于测试或诊断。

(8)CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。

GET方式和POST方式对比:

GET方式:请求数据放在HTTP包头;使用明文传送,不安全;长度较小,一般为1024B;应用的场景为查询数据;如果传送的是英文数字或者是数字,直接发送,如果传送的是中文字符或则是其他的字符,则会进行BASE64编码

POST方式:请求数据放在HTTP正文;可明文或者密文传送,较为安全;长度一般没有限制;应用在修改数据上。

149. http1.0 http1.1的区别

(1)HTTP1.0 规定浏览与服务器只是保持短暂的连接,浏览器每次请求都需要和服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不去跟踪每个客户也不去记录每个客户过去的请求。HTTP1.0 不支持HOST请求字段

(2)HTTP1.1支持久连接,在一个TCP上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟;

允许客户端不用等待上一次请求的返回结果,就可以去发送下一个请求,但是服务器端必须按照接受的客户端请求的先后顺序依次会送响应的结果,这样客户端才能够区分出每次请求的响应的内容。

HTTP1.0 支持HOST请求字段,这样就可以使用一个IP地址和端口号,在此基础上使用不同的主机名创建多个虚拟的WEB站点;

HTTP1.1 提供了与身份认证、状态管理和cache缓存等机制

150. http怎么去处理长连接

http1.1 默认支持长连接,即一次tcp连接,允许发送多个http请求。

当web服务器看到keep-alive值时,会建立长连接。

151. 电脑上访问一个网页的整个过程是怎样的?DNSHTTPTCPOSPFIPARP

步骤1:当访问www.baidu.com时,会先从本地的host文件中获取该域名对应的IP地址,如果找不到就会用DNS协议来获取IP,在该DNS协议中,计算机会由本地的DNS服务器来解析该域名,最终找到对应的IP地址。

步骤2:接下来是使用TCP协议,建立TCP连接,在建立连接之前需要,为了将给服务器的消息带给服务器,则需要OSPF\IP\ARP协议的支持,IP告诉该消息从哪里出发,去向那里;消息的传送会经过一个个的路由器,OSPF会利用路由算法找出最佳的通往目的地址的路径;ARP负责找到下一个节点的地址,ARP协议使用的MAC地址,整个的发送的过程涉及到每一个节点的MAP地址。

步骤3:通过步骤2的解析IP,现在可以和服务器建立TCP连接了,这时客户端便可以将Http请求数据发送给服务器端。服务器端进行处理,然后以http response的形式发送给客户端。

152. IP地址的分类

A类地址:1个字节的网络号+3个字节的主机地址  0.0.0.0~126.255.255.255

B类地址:2个字节的网络号+2个字节的主机地址  128.0.0.0~191.255.255.255

C类地址:3个字节的网络号+1个字节的主机地址  192.0.0.0~223.255.255.255

D类地址:多播地址

E类地址:保留为今后使用

153. 路由器和交换机的区别

交换机:为数据桢从一个端口到另外一个端口的转发提供了低时延、低开销的通路,使得任意端口接受的数据帧都能够从其他的端口送出。

路由器:网络连接和路由选择,用于网络层的数据转发。

154. 如何设计一个高并发的系统?

① 数据库的优化,包括合理的事务隔离级别、SQL语句优化、索引的优化

使用缓存,尽量减少数据库 IO

③ 分布式数据库、分布式缓存

④ 服务器的负载均衡

155. 设计模式简单工厂模式

有一个抽象的产品父类将所有的具体的产品抽象出来,达到复用的目的。同时有一个简单工厂维护一个对抽象产品的依赖,在该简单工厂中去负责实例的创建,在该工厂中去实例不同的对象,往往需要利用case判断语句去动态实例化相关的类。

工厂方法模式

创建对象的接口,让子类去决定具体实例化的对象,把简单的内部逻辑的判断,转移到了客户端,让客户端去动态地实例化相关的子类。工厂方法模式克服了简单工厂违背开放-封闭原则的特点。

抽象工厂模式

提供创建一系列相关或者相互依赖对象的接口,而无需指定他们具体的类。

职责链模式

使得多个对象都有机会去处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链去传递该请求,直到有一个对象处理它为之。

单例模式

2)恶汉式的单例模式

利用静态static的方式进行实例化,在类被加载时就会创建实例。

/**

 * 饿汉式实现单例模式

 */

public class Singleton {

  private static Singleton instance = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {

        return instance;

    }

}

(6)懒汉式实现单例模式

在被第一次引用时才去创建对象。

/**

 * 懒汉式实现单例模式

 */

public class Singleton {

    private static Singleton instance;//创建私有的静态变量

    private Singleton() {//私有的构造函数

    }

    // synchronized方法,多线程情况下保证单例对象唯一

public static synchronized Singleton getInstance() {

//如果实例对象为空,就重新去实例化

        if (instance == null) {

            instance = new Singleton();

        }

        return instance;

    }

}

分析:这中方法的实现,效率不高,因为该方法定义为同步的方法。

(7)双重锁实现的单例模式

/**

 * DCL实现单例模式

 */

public class Singleton {

    private static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {

        // 两层判空,第一层是为了避免不必要的同步

        // 第二层是为了在null的情况下创建实例

        if (instance == null) {

            synchronized (Singleton.class) {

                if (instance == null) {

                    instance = new Singleton();

                }

            }

        }

        return instance;

    }

}

分析:资源的利用率较高,在需要的时候去初始化实例,而且可以保证线程的安全,该方法没有去进行同步锁,效率比较好。

(8)静态内部类实现单例模式

/**

 * 静态内部类实现单例模式

 */

public class Singleton {

    private Singleton() {

}

//返回实例的方法

    public static Singleton getInstance() {

        return SingletonHolder.instance;

    }

    /**

     * 静态内部类

     */

private static class SingletonHolder {

    //静态私有的实例对象

        private static Singleton instance = new Singleton();

    }

}

分析:第一次加载类时不会去初始化instance,只有第一次调用getInstance()方法时,虚拟机才会加载内部类,初始化instance

可以保证线程的安全,单例对象的唯一,延迟了单例的初始化。

(9)枚举单例

/**

 * 枚举实现单例模式

 */

public enum SingletonEnum {

    INSTANCE;

    public void doSomething() {

        System.out.println("do something");

    }

}

分析:枚举实例的创建是线程安全的,即使反序列化也不会生成新的实例,在任何的情况下都是单例的。

适配器模式

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口步兼容而不能一起工作的类变得可以一起工作。

target是我们所期望的接口的类型,包含一个request方法,通过使用adapter去实现该接口,并实现其中的request方法,在adapter中建立一个私有的adaptee对象,在adapter重写的方法中去调用specificRequest方法,这样适配器adapter就构建好了。只需要在客户端,创建adapter实例,调用request方法就可以利用多态的方式,实现了specificRequest()方法。

观察者模式

定义了一种一对多的依赖关系,让多个观察者可以同时去监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有的观察者对象,使得他们能够自动更新自己。

Subject:把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者,可以增加删除观察者对象。

Observer:抽象观察者,为所有的具体的观察者定义一个接口,在得到主题时更新自己。

concreteObserver:具体的观察者,实现更新的方法

concreteSubject:具体的主题

作用:应用在一个对象改变时,需要改变其他的对象,而且具体不知道有多少个对象需要改变,将耦合的双方都依赖于抽象而不是依赖于具体,从而使得各自的变化都不会影响到另外一边的变化。

大整数BigInteger  大浮点数 BigDecimal

java运算的优先级:优先级相同时比较结合性:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

java 高并发

156. 1、线程与进程

进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。

一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

区别不同 
a,地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间; 
b,资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资 
c,线程是处理器调度的基本单位,但进程不是. 
d,二者均可并发执行.

157. 2、 守护线程

Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。 
守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

158. 3、java thread状态

NEW 状态是指线程刚创建, 尚未启动

RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等

BLOCKED 这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 也就是这里是线程在等待进入临界区

WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束

TIMED_WAITING 这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态

TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)

159. 4、请说出与线程同步以及线程调度相关的方法。

wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;

notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;

notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

160. 5、进程调度算法

实时系统FIFO(First Input First Output,先进先出算法),SJF(Shortest Job First,最短作业优先算法),SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。 
交互式系统RR(Round Robin,时间片轮转算法),HPF(Highest Priority First,最高优先级算法),多级队列,最短进程优先,保证调度,彩票调度,公平分享调度。

161. 6、wait()和sleep()的区别

sleep来自Thread类,和wait来自Object类

调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁

sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU

sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒

162. 7、ThreadLocal,以及死锁分析

hreadLocal为每个线程维护一个本地变量。 
采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。 
ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。 
彻底理解ThreadLocal

163. 8、Synchronized 与Lock

ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候 
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定, 
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断 
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

ReentrantLock获取锁定与三种方式: 
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁 
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false; 
c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false; 
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

总体的结论先摆出来:

synchronized: 
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronized,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。 
ReentrantLock: 
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

164. 9、Volatile和Synchronized

Volatile和Synchronized四个不同点:

粒度不同,前者针对变量 ,后者锁对象和类

syn阻塞,volatile线程不阻塞

syn保证三大特性,volatile不保证原子性

syn编译器优化,volatile不优化 
要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件: 

对变量的写操作不依赖于当前值。

该变量没有包含在具有其他变量的不变式中。

165. 10、CAS

CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

166. 11、Java中Unsafe类详解

通过Unsafe类可以分配内存,可以释放内存;类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应。

可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;

挂起与恢复:将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。

cas 
Java中Unsafe类详解

167. 12、线程池

线程池的作用: 
在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程 
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。 
第三:提高线程的可管理性。 
常用线程池:ExecutorService 是主要的实现类,其中常用的有 
Executors.newSingleT 
hreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。

168. 13、ThreadPoolExecutor

构造方法参数说明

corePoolSize:核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。 
maximumPoolSize:线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。 
keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收。 
unit:指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。 
workQueue:线程池中的任务队列. 
常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。

threadFactory:线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法

原理

如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。

如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列

如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。

如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。

线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。

13、Executor拒绝策略

AbortPolicy:为java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try 
catch,否则程序会直接退出.

DiscardPolicy:直接抛弃,任务不执行,空方法

DiscardOldestPolicy:从队列里面抛弃head的一个任务,并再次execute 此task。

CallerRunsPolicy:在调用execute的线程里面执行此command,会阻塞入

用户自定义拒绝策略:实现RejectedExecutionHandler,并自己定义策略模式

169. 14、CachedThreadPool 、 FixedThreadPool、SingleThreadPool

newSingleThreadExecutor :创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行 
适用场景:任务少 ,并且不需要并发执行

newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. 
线程没有任务要执行时,便处于空闲状态,处于空闲状态的线程并不会被立即销毁(会被缓存住),只有当空闲时间超出一段时间(默认为60s)后,线程池才会销毁该线程(相当于清除过时的缓存)。新任务到达后,线程池首先会让被缓存住的线程(空闲状态)去执行任务,如果没有可用线程(无空闲线程),便会创建新的线程。 
适用场景:处理任务速度 > 提交任务速度,耗时少的任务(避免无限新增线程)

newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行

170. 15、CopyOnWriteArrayList

CopyOnWriteArrayList : 写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,然后在新的容器里面写,写完之后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,所以可以进行并发的读,但这是一种弱一致性的策略。 
使用场景:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

16、AQS

AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。

private volatile int state;//共享变量,使用volatile修饰保证线程可见性

1

2种同步方式:独占式,共享式。独占式如ReentrantLock,共享式如Semaphore,CountDownLatch,组合式的如ReentrantReadWriteLock

节点的状态 
CANCELLED,值为1,表示当前的线程被取消; 
SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark; 
CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中; 
PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行; 
值为0,表示当前节点在sync队列中,等待着获取锁。

模板方法模式 
 protected boolean tryAcquire(int arg) : 独占式获取同步状态,试着获取,成功返回true,反之为false 
 protected boolean tryRelease(int arg) :独占式释放同步状态,等待中的其他线程此时将有机会获取到同步状态; 
 protected int tryAcquireShared(int arg) :共享式获取同步状态,返回值大于等于0,代表获取成功;反之获取失败; 
 protected boolean tryReleaseShared(int arg) :共享式释放同步状态,成功为true,失败为false 
AQS维护一个共享资源state,通过内置的FIFO来完成获取资源线程的排队工作。该队列由一个一个的Node结点组成,每个Node结点维护一个prev引用和next引用,分别指向自己的前驱和后继结点。双端双向链表。 

独占式:乐观的并发策略 
acquire 
 a.首先tryAcquire获取同步状态,成功则直接返回;否则,进入下一环节; 
b.线程获取同步状态失败,就构造一个结点,加入同步队列中,这个过程要保证线程安全; 
 c.加入队列中的结点线程进入自旋状态,若是老二结点(即前驱结点为头结点),才有机会尝试去获取同步状态;否则,当其前驱结点的状态为SIGNAL,线程便可安心休息,进入阻塞状态,直到被中断或者被前驱结点唤醒。 
release 
release的同步状态相对简单,需要找到头结点的后继结点进行唤醒,若后继结点为空或处于CANCEL状态,从后向前遍历找寻一个正常的结点,唤醒其对应线程。

共享式
共享式地获取同步状态.同步状态的方法tryAcquireShared返回值为int。 
a.当返回值大于0时,表示获取同步状态成功,同时还有剩余同步状态可供其他线程获取; 
 b.当返回值等于0时,表示获取同步状态成功,但没有可用同步状态了; 
 c.当返回值小于0时,表示获取同步状态失败。

AQS实现公平锁和非公平锁 
非公平锁中,那些尝试获取锁且尚未进入等待队列的线程会和等待队列head结点的线程发生竞争。公平锁中,在获取锁时,增加了isFirst(current)判断,当且仅当,等待队列为空或当前线程是等待队列的头结点时,才可尝试获取锁。 
 Java并发包基石-AQS详解

171. 16、Java里的阻塞队列

7个阻塞队列。分别是

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。 
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。 
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。 
DelayQueue:一个使用优先级队列实现的无界阻塞队列。 
SynchronousQueue:一个不存储元素的阻塞队列。 
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。 
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

添加元素

Java中的阻塞队列接口BlockingQueue继承自Queue接口。BlockingQueue接口提供了3个添加元素方法。 
add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会抛出IllegalStateException异常 
offer:添加元素到队列里,添加成功返回true,添加失败返回false 
put:添加元素到队列里,如果容量满了会阻塞直到容量不满

删除方法

3个删除方法 
poll:删除队列头部元素,如果队列为空,返回null。否则返回元素。 
remove:基于对象找到对应的元素,并删除。删除成功返回true,否则返回false 
take:删除队列头部元素,如果队列为空,一直阻塞到队列有元素并删除

172. 17、condition

Condition的源码理解,主要就是理解等待队列,等待队列可以类比同步队列,而且等待队列比同步队列要简单,因为等待队列是单向队列,同步队列是双向队列。

java condition使用及分

173. 18、DelayQueue

队列中每个元素都有个过期时间,并且队列是个优先级队列,当从队列获取元素时候,只有过期元素才会出队列。

并发队列-无界阻塞延迟队列delayqueue原理探究

174. 19、Fork/Join框架

 Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。Fork/Join框架要完成两件事情:

  1.任务分割:首先Fork/Join框架需要把大的任务分割成足够小的子任务,如果子任务比较大的话还要对子任务进行继续分割

  2.执行任务并合并结果:分割的子任务分别放到双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都放在另外一个队列里,启动一个线程从队列里取数据,然后合并这些数据。

  在Java的Fork/Join框架中,使用两个类完成上述操作

  1.ForkJoinTask:我们要使用Fork/Join框架,首先需要创建一个ForkJoin任务。该类提供了在任务中执行fork和join的机制。通常情况下我们不需要直接集成ForkJoinTask类,只需要继承它的子类,Fork/Join框架提供了两个子类:

    a.RecursiveAction:用于没有返回结果的任务

    b.RecursiveTask:用于有返回结果的任务

  2.ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行

  任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务(工作窃取算法)。 
Fork/Join框架的实现原理 
  ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成,ForkJoinTask数组负责将存放程序提交给ForkJoinPool,而ForkJoinWorkerThread负责执行这

20、原子操作类

java.util.concurrent.atomic包下,可以分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。

原子更新基本类型 
使用原子方式更新基本类型,共包括3个类: 
AtomicBoolean:原子更新布尔变量 
AtomicInteger:原子更新整型变量 
AtomicLong:原子更新长整型变量

原子更新数组 
通过原子更新数组里的某个元素,共有3个类: 
AtomicIntegerArray:原子更新整型数组的某个元素 
AtomicLongArray:原子更新长整型数组的某个元素 
AtomicReferenceArray:原子更新引用类型数组的某个元素 
AtomicIntegerArray常用的方法有: 
int addAndSet(int i, int delta):以原子方式将输入值与数组中索引为i的元素相加 
boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值

原子更新引用类型 
AtomicReference:原子更新引用类型 
AtomicReferenceFieldUpdater:原子更新引用类型里的字段 
AtomicMarkableReference:原子更新带有标记位的引用类型。

原子更新字段类 
如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类: 
AtomicIntegerFieldUpdater:原子更新整型字段 
AtomicLongFieldUpdater:原子更新长整型字段 
AtomicStampedReference:原子更新带有版本号的引用类型。 
要想原子更新字段,需要两个步骤: 
每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段 
更新类的字段(属性)必须为public volatile

21、同步屏障CyclicBarrier

CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。 
CyclicBarrier和CountDownLatch的区别

CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。 
CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。

175. 22、Semaphore

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源 
Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,我们就可以使用Semaphore来做流控,代码如下:

控制并发线程数的Semaphore

176. 23、死锁,以及解决死锁

死锁产生的四个必要条件

互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。 
不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。 
请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。 
循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。

解决死锁

一是死锁预防,就是不让上面的四个条件同时成立。 
二是,合理分配资源。 
三是使用银行家算法,如果该进程请求的资源操作系统剩余量可以满足,那么就分配。

177. 24、进程间的通信方式

管道( pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

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

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

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

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

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

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

中断

interrupt()的作用是中断本线程。 
本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。 
如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。 
如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。 
如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。 
中断一个“已终止的线程”不会产生任何操作。

终止处于“阻塞状态”的线程 
通常,我们通过“中断”方式终止处于“阻塞状态”的线程。 
当线程由于被调用了sleep(), wait(), join()等方法而进入阻塞状态;若此时调用线程的interrupt()将线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除,同时产生一个InterruptedException异常。将InterruptedException放在适当的为止就能终止线程, 

终止处于“运行状态”的线程

interrupted() 和 isInterrupted()的区别

最后谈谈 interrupted() 和 isInterrupted()。 
interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。 
区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。 
interrupt()和线程终止方式

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

178. 1、常用设计模式

单例模式:懒汉式、饿汉式、双重校验锁、静态加载,内部类加载、枚举类加载。保证一个类仅有一个实例,并提供一个访问它的全局访问点。

代理模式:动态代理和静态代理,什么时候使用动态代理。

适配器模式:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

装饰者模式:动态给类加功能。

观察者模式:有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

外观模式:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

命令模式:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

创建者模式:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

 

179. 2、基础知识

Java基本类型哪些,所占字节和范围

Set、List、Map的区别和联系

什么时候使用Hashmap

什么时候使用Linkedhashmap、Concurrenthashmap、Weakhashmap

哪些集合类是线程安全的

为什么Set、List、map不实现Cloneable和Serializable接口

Concurrenthashmap的实现,1.7和1.8的实现

Arrays.sort的实现

什么时候使用CopyOnArrayList

volatile的使用

synchronied的使用

reentrantlock的实现和Synchronied的区别

CAS的实现原理以及问题

AQS的实现原理

接口和抽象类的区别,什么时候使用

类加载机制的步骤,每一步做了什么,static和final修改的成员变量的加载时机

双亲委派模型

反射机制:反射动态擦除泛型、反射动态调用方法等

动态绑定:父类引用指向子类对象

JVM内存管理机制:有哪些区域,每个区域做了什么

JVM垃圾回收机制:垃圾回收算法 垃圾回收器 垃圾回收策略

jvm参数的设置和jvm调优

什么情况产生年轻代内存溢出、什么情况产生年老代内存溢出

内部类:静态内部类和匿名内部类的使用和区别

Redismemcached:什么时候选择redis,什么时候选择memcached,内存模型和存储策略是什么样的

MySQL的基本操作 主从数据库一致性维护

mysql的优化策略有哪些

mysql索引的实现 B+树的实现原理

什么情况索引不会命中,会造成全表扫描

java中bio nio aio的区别和联系

为什么bio是阻塞的 nio是非阻塞的 nio是模型是什么样的

Java io的整体架构和使用的设计模式

Reactor模型和Proactor模型

http请求报文结构和内容

http三次握手和四次挥手

rpc相关:如何设计一个rpc框架,从io模型 传输协议 序列化方式综合考虑

Linux命令 统计,排序,前几问题等

StringBuff 和StringBuilder的实现,底层实现是通过byte数据,外加数组的拷贝来实现的

cas操作的使用

内存缓存和数据库的一致性同步实现

微服务的优缺点

线程池的参数问题

ip问题 如何判断ip是否在多个ip段中

判断数组两个中任意两个数之和是否为给定的值

乐观锁和悲观锁的实现

synchronized实现原理

你在项目中遇到的困难和怎么解决的

你在项目中完成的比较出色的亮点

消息队列广播模式和发布/订阅模式的区别

生产者消费者代码实现

死锁代码实现

线程池:参数,每个参数的作用,几种不同线程池的比较,阻塞队列的使用,拒绝策略

Future和ListenableFuture 异步回调相关

算法相关:判断能否从数组中找出两个数字和为给定值,随机生成1~10000不重复并放入数组,求数组的子数组的最大和,二分查找算法的实现及其时间复杂计算

 

180. 3、其它

算法:常用排序算法,二分查找,链表相关,数组相关,字符串相关,树相关等

常见序列化协议及其优缺点

memcached内存原理,为什么是基于块的存储

搭建一个rpc需要准备什么

如果线上服务器频繁地出现full gc ,如何去排查

如果某一时刻线上机器突然量变得很大,服务扛不住了,怎么解决

LUR算法的实现

LinkedHashMap实现LRU

定义栈的数据结构,请在该类型中实现一个能够找到栈最小元素的min函数

海量数据处理的解决思路

reactor模型的演变

阻塞、非阻塞、同步、异步区别

Collection的子接口

jvm调优相关

zookeeper相关,节点类型,如何实现服务发现和服务注册

nginx负载均衡相关,让你去实现负载均衡,该怎么实现

linux命令,awk、cat、sort、cut、grep、uniq、wc、top等

压力测试相关,怎么分析,单接口压测和多情况下的压测

你觉得你的有点是什么,你的缺点是什么

spring mvc的实现原理

netty底层实现,IO模型,ChannelPipeline的实现和原理

缓存的设计和优化

缓存和数据库一致性同步解决方案

你所在项目的系统架构,谈谈整体实现

消息队列的使用场景

ActiveMQ、RabbitMQ、Kafka的区别

 

 

 

 

 

 

 

 

 

 

181. 一、Java基础

1、String类为什么是final的。

2、HashMap的源码,实现原理,底层结构。

3、说说你知道的几个Java集合类:list、set、queue、map实现类咯。。。

4、描述一下ArrayList和LinkedList各自实现和区别

5、Java中的队列都有哪些,有什么区别。

6、反射中,Class.forName和classloader的区别

7、Java7、Java8的新特性(baidu问的,好BT)

8、Java数组和链表两种结构的操作效率,在哪些情况下(从开头开始,从结尾开始,从中间开始),哪些操作(插入,查找,删除)的效率高

9、Java内存泄露的问题调查定位:jmap,jstack的使用等等

10、string、stringbuilder、stringbuffer区别

11、hashtable和hashmap的区别

13、异常的结构,运行时异常和非运行时异常,各举个例子

14、String a= “abc” String b = "abc" String c = new String("abc") String d = "ab" + "c" .他们之间用 == 比较的结果

15、String 类的常用方法

16、Java 的引用类型有哪几种

17、抽象类和接口的区别

18、java的基础类型和字节大小。

19、Hashtable,HashMap,ConcurrentHashMap 底层实现原理与线程安全问题(建议熟悉 jdk 源码,才能从容应答)

20、如果不让你用Java Jdk提供的工具,你自己实现一个Map,你怎么做。说了好久,说了HashMap源代码,如果我做,就会借鉴HashMap的原理,说了一通HashMap实现

21、 Hash冲突怎么办?哪些解决散列冲突的方法?

22、HashMap冲突很厉害,最差性能,你会怎么解决?从O(n)提升到log(n)咯,用二叉排序树的思路说了一通

23、rehash

24、hashCode() 与 equals() 生成算法、方法怎么重写

182. 二、Java IO

1、讲讲IO里面的常见类,字节流、字符流、接口、实现类、方法阻塞。

2、讲讲NIO。

3、String 编码UTF-8 和GBK的区别?

4、什么时候使用字节流、什么时候使用字符流?

5、递归读取文件夹下的文件,代码怎么实现

183. 三、Java Web

1、session和cookie的区别和联系,session的生命周期,多个服务部署时session管理。

2、servlet的一些相关问题

3、webservice相关问题

4、jdbc连接,forname方式的步骤,怎么声明使用一个事务。举例并具体代码

5、无框架下配置web.xml的主要配置内容

6、jsp和servlet的区别 

184. 四、JVM

1、Java的内存模型以及GC算法

2、jvm性能调优都做了什么

3、介绍JVM中7个区域,然后把每个区域可能造成内存的溢出的情况说明

4、介绍GC 和GC Root不正常引用。

5、自己从classload 加载方式,加载机制说开去,从程序运行时数据区,讲到内存分配,讲到String常量池,讲到JVM垃圾回收机制,算法,hotspot。反正就是各种扩展

6、jvm 如何分配直接内存, new 对象如何不分配在堆而是栈上,常量池解析

7、数组多大放在 JVM 老年代(不只是设置 PretenureSizeThreshold ,问通常多大,没做过一问便知)

8、老年代中数组的访问方式

9、GC 算法,永久代对象如何 GC , GC 有环怎么处理

10、谁会被 GC ,什么时候 GC

11、如果想不被 GC 怎么办

12、如果想在 GC 中生存 1 次怎么办

185. 五、开源框架

1、hibernate和ibatis的区别

2、讲讲mybatis的连接池。

3、spring框架中需要引用哪些jar包,以及这些jar包的用途

4. springMVC的原理

5、springMVC注解的意思

6、spring中beanFactory和ApplicationContext的联系和区别

7、spring注入的几种方式(循环注入)

8、spring如何实现事物管理的

9、springIOC

10、spring AOP的原理

11、hibernate中的1级和2级缓存的使用方式以及区别原理(Lazy-Load的理解)

12、Hibernate的原理体系架构,五大核心接口,Hibernate对象的三种状态转换,事务管理。

186. 六、多线程

1、Java创建线程之后,直接调用start()方法和run()的区别

2、常用的线程池模式以及不同线程池的使用场景

3、newFixedThreadPool此种线程池如果线程数达到最大值后会怎么办,底层原理。

4、多线程之间通信的同步问题,synchronized锁的是对象,衍伸出和synchronized相关很多的具体问题,例如同一个类不同方法都有synchronized锁,一个对象是否可以同时访问。或者一个类的static构造方法加上synchronized之后的锁的影响。

5、了解可重入锁的含义,以及ReentrantLock 和synchronized的区别

6、同步的数据结构,例如concurrentHashMap的源码理解以及内部实现原理,为什么他是同步的且效率高

7、atomicinteger和volatile等线程安全操作的关键字的理解和使用

8、线程间通信,wait和notify

9、定时线程的使用

10、场景:在一个主线程中,要求有大量(很多很多)子线程执行完之后,主线程才执行完成。多种方式,考虑效率。

11、进程和线程的区别

12、什么叫线程安全?举例说明

13、线程的几种状态

14、并发、同步的接口或方法

15、HashMap 是否线程安全,为何不安全。 ConcurrentHashMap,线程安全,为何安全。底层实现是怎么样的。

16、J.U.C下的常见类的使用。 ThreadPool的深入考察; BlockingQueue的使用。(take,poll的区别,put,offer的区别);原子类的实现。

17、简单介绍下多线程的情况,从建立一个线程开始。然后怎么控制同步过程,多线程常用的方法和结构

18、volatile的理解

19、实现多线程有几种方式,多线程同步怎么做,说说几个线程里常用的方法

187. 七、网络通信

1、http是无状态通信,http的请求方式有哪些,可以自己定义新的请求方式么。

2、socket通信,以及长连接,分包,连接异常断开的处理。

3、socket通信模型的使用,AIO和NIO。

4、socket框架netty的使用,以及NIO的实现原理,为什么是异步非阻塞。

5、同步和异步,阻塞和非阻塞。

6、OSI七层模型,包括TCP,IP的一些基本知识

7、http中,get post的区别

8、说说http,tcp,udp之间关系和区别。

9、说说浏览器访问www.taobao.com,经历了怎样的过程。

10、HTTP协议、  HTTPS协议,SSL协议及完整交互过程;

11、tcp的拥塞,快回传,ip的报文丢弃

12、https处理的一个过程,对称加密和非对称加密

13、head各个特点和区别

14、说说浏览器访问www.taobao.com,经历了怎样的过程。

188. 八、数据库MySql

1、MySql的存储引擎的不同

2、单个索引、联合索引、主键索引

3、Mysql怎么分表,以及分表后如果想按条件分页查询怎么办(如果不是按分表字段来查询的话,几乎效率低下,无解)

4、分表之后想让一个id多个表是自增的,效率实现

5、MySql的主从实时备份同步的配置,以及原理(从库读主库的binlog),读写分离

6、写SQL语句。。。

7、索引的数据结构,B+树

8、事务的四个特性,以及各自的特点(原子、隔离)等等,项目怎么解决这些问题

9、数据库的锁:行锁,表锁;乐观锁,悲观锁

10、数据库事务的几种粒度;

11、关系型和非关系型数据库区别

189. 九、设计模式 

1、单例模式:饱汉、饿汉。以及饿汉中的延迟加载,双重检查

2、工厂模式、装饰者模式、观察者模式。

3、工厂方法模式的优点(低耦合、高内聚,开放封闭原则) 

190. 十、算法 

1、使用随机算法产生一个数,要求把1-1000W之间这些数全部生成。(考察高效率,解决产生冲突的问题)

2、两个有序数组的合并排序

3、一个数组的倒序

4、计算一个正整数的正平方根

5、说白了就是常见的那些查找、排序算法以及各自的时间复杂度

6、二叉树的遍历算法

7、DFS,BFS算法

9、比较重要的数据结构,如链表,队列,栈的基本理解及大致实现。

10、排序算法与时空复杂度(快排为什么不稳定,为什么你的项目还在用)

11、逆波兰计算器

12、Hoffman 编码

13、查找树与红黑树 

191. 十一、并发与性能调优 

1、有个每秒钟5k个请求,查询手机号所属地的笔试题(记得不完整,没列出),如何设计算法?请求再多,比如5w,如何设计整个系统?

2、高并发情况下,我们系统是如何支撑大量的请求的

3、集群如何同步会话状态

4、负载均衡的原理

5、如果有一个特别大的访问量,到数据库上,怎么做优化(DB设计,DBIO,SQL优化,Java优化)

6、如果出现大面积并发,在不增加服务器的基础上,如何解决服务器响应不及时问题“。

7、假如你的项目出现性能瓶颈了,你觉得可能会是哪些方面,怎么解决问题。

8、如何查找 造成 性能瓶颈出现的位置,是哪个位置照成性能瓶颈。

9、你的项目中使用过缓存机制吗?有没用用户非本地缓存 

192. 十二、其他 

1、 常用的linux下的命令

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

SPRING

193. 1、什么是Spring框架?Spring框架有哪些主要模块?

Spring框架是一个为Java应用程序的开发提供了综合、广泛的基础性支持的Java平台。

Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发。

Spring框架本身亦是按照设计模式精心打造,这使得我们可以在开发环境中安心的集成Spring框架,不必担心Spring是如何在后台进行工作的。

Spring框架至今已集成了20多个模块。这些模块主要被分如下图所示的核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。

 

194. 2、使用Spring框架能带来哪些好处?

下面列举了一些使用Spring框架带来的主要好处:

  • Dependency Injection(DI) 方法使得构造器和JavaBean properties文件中的依赖关系一目了然。
  • EJB容器相比较,IoC容器更加趋向于轻量级。这样一来IoC容器在有限的内存和CPU资源的情况下进行应用程序的开发和发布就变得十分有利。
  • Spring并没有闭门造车,Spring利用了已有的技术比如ORM框架、logging框架、J2EE、Quartz和JDK Timer,以及其他视图技术。
  • Spring框架是按照模块的形式来组织的。由包和类的编号就可以看出其所属的模块,开发者仅仅需要选用他们需要的模块即可。
  • 测试一项用Spring开发的应用程序十分简单,因为测试相关的环境代码都已经囊括在框架中了。更加简单的是,利用JavaBean形式的POJO类,可以很方便的利用依赖注入来写入测试数据。
  • Spring的Web框架亦是一个精心设计的Web MVC框架,为开发者们在web框架的选择上提供了一个除了主流框架比如Struts、过度设计的、不流行web框架的以外的有力选项。
  • Spring提供了一个便捷的事务管理接口,适用于小型的本地事物处理(比如在单DB的环境下)和复杂的共同事物处理(比如利用JTA的复杂DB环境)。

195. 3、什么是控制反转(IOC)?什么是依赖注入?

控制反转是应用于软件工程领域中的,在运行时被装配器对象来绑定耦合对象的一种编程技巧,对象之间耦合关系在编译时通常是未知的。在传统的编程方式中,业务逻辑的流程是由应用程序中的早已被设定好关联关系的对象来决定的。在使用控制反转的情况下,业务逻辑的流程是由对象关系图来决定的,该对象关系图由装配器负责实例化,这种实现方式还可以将对象之间的关联关系的定义抽象化。而绑定的过程是通过“依赖注入”实现的。

控制反转是一种以给予应用程序中目标组件更多控制为目的设计范式,并在我们的实际工作中起到了有效的作用。

依赖注入是在编译阶段尚未知所需的功能是来自哪个的类的情况下,将其他对象所依赖的功能对象实例化的模式。这就需要一种机制用来激活相应的组件以提供特定的功能,所以依赖注入是控制反转的基础。否则如果在组件不受框架控制的情况下,框架又怎么知道要创建哪个组件?

Java中依然注入有以下三种实现方式:

  1. 构造器注入
  2. Setter方法注入
  3. 接口注入

196. 4、请解释下Spring框架中的IoC?

Spring中的 org.springframework.beans 包和 org.springframework.context包构成了Spring框架IoC容器的基础。

BeanFactory 接口提供了一个先进的配置机制,使得任何类型的对象的配置成为可能。ApplicationContex接口对BeanFactory(是一个子接口)进行了扩展,在BeanFactory的基础上添加了其他功能,比如与Spring的AOP更容易集成,也提供了处理message resource的机制(用于国际化)、事件传播以及应用层的特别配置,比如针对Web应用的WebApplicationContext。

org.springframework.beans.factory.BeanFactory 是Spring IoC容器的具体实现,用来包装和管理前面提到的各种bean。BeanFactory接口是Spring IoC 容器的核心接口。

IOC:把对象的创建、初始化、销毁交给spring来管理,而不是由开发者控制,实现控制反转。

 

197. 5、BeanFactory和ApplicationContext有什么区别?

BeanFactory 可以理解为含有bean集合的工厂类。BeanFactory 包含了种bean的定义,以便在接收到客户端请求时将对应的bean实例化。

BeanFactory还能在实例化对象的时生成协作类之间的关系。此举将bean自身与bean客户端的配置中解放出来。BeanFactory还包含了bean生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。

从表面上看,application context如同bean factory一样具有bean定义、bean关联关系的设置,根据请求分发bean的功能。但applicationcontext在此基础上还提供了其他的功能。

  1. 提供了支持国际化的文本消息
  2. 统一的资源文件读取方式
  3. 已在监听器中注册的bean的事件

以下是三种较常见的 ApplicationContext 实现方式:

1、ClassPathXmlApplicationContext:从classpath的XML配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得。

[html] view plain copy

  1. ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);    

2、FileSystemXmlApplicationContext :由文件系统中的XML配置文件读取上下文。

[html] view plain copy

  1. ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”);   

3、XmlWebApplicationContext:由Web应用的XML文件读取上下文。

 

198. 6、Spring有几种配置方式?

Spring配置到应用开发中有以下三种方式:

  1. 基于XML的配置
  2. 基于注解的配置
  3. 基于Java的配置

199. 7、如何用基于XML配置的方式配置Spring?

Spring框架中,依赖和服务需要在专门的配置文件来实现,我常用的XML格式的配置文件。这些配置文件的格式通常用<beans>开头,然后一系列的bean定义和专门的应用配置选项组成。

SpringXML配置的主要目的时候是使所有的Spring组件都可以用xml文件的形式来进行配置。这意味着不会出现其他的Spring配置类型(比如声明的方式或基于Java Class的配置方式)

Spring的XML配置方式是使用被Spring命名空间的所支持的一系列的XML标签来实现的。Spring有以下主要的命名空间:context、beans、jdbc、tx、aop、mvc和aso。

如:

[html] view plain copy

  1. <beans>    
  2. <!-- JSON Support -->    
  3. <bean name="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/>    
  4. <bean name="jsonTemplate" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>    
  5. <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/>    
  6. </beans>   

下面这个web.xml仅仅配置了DispatcherServlet,这件最简单的配置便能满足应用程序配置运行时组件的需求。

 

200. 8、如何用基于Java配置的方式配置Spring?

Spring对Java配置的支持是由@Configuration注解和@Bean注解来实现的。由@Bean注解的方法将会实例化、配置和初始化一个新对象,这个对象将由Spring的IoC容器来管理。@Bean声明所起到的作用与<bean/> 元素类似。被@Configuration所注解的类则表示这个类的主要目的是作为bean定义的资源。被@Configuration声明的类可以通过在同一个类的内部调用@bean方法来设置嵌入bean的依赖关系。

最简单的@Configuration 声明类请参考下面的代码:

[java] view plain copy

  1. @Configuration    
  2. public class AppConfig{    
  3. @Bean    
  4. public MyService myService() {    
  5. return new MyServiceImpl();    
  6. }    
  7. }   

对于上面的@Beans配置文件相同的XML配置文件如下:

[html] view plain copy

  1. <beans>    
  2. <bean id="myService" class="com.somnus.services.MyServiceImpl"/>    
  3. </beans>   

上述配置方式的实例化方式如下:利用AnnotationConfigApplicationContext 类进行实例化

 

要使用组件组建扫描,仅需用@Configuration进行注解即可:

[java] view plain copy

  1. @Configuration    
  2. @ComponentScan(basePackages = "com.somnus")    
  3. public class AppConfig  {    
  4. ...    
  5. }   

在上面的例子中,com.acme包首先会被扫到,然后再容器内查找被@Component 声明的类,找到后将这些类按照Sring bean定义进行注册。

如果你要在你的web应用开发中选用上述的配置的方式的话,需要用AnnotationConfigWebApplicationContext 类来读取配置文件,可以用来配置Spring的Servlet监听器ContextLoaderListener或者Spring MVC的DispatcherServlet。

201. 9、怎样用注解的方式配置Spring?

Spring在2.5版本以后开始支持用注解的方式来配置依赖注入。可以用注解的方式来替代XML方式的bean描述,可以将bean描述转移到组件类的内部,只需要在相关类上、方法上或者字段声明上使用注解即可。注解注入将会被容器在XML注入之前被处理,所以后者会覆盖掉前者对于同一个属性的处理结果。

注解装配在Spring中是默认关闭的。所以需要在Spring文件中配置一下才能使用基于注解的装配模式。如果你想要在你的应用程序中使用关于注解的方法的话,请参考如下的配置。

[html] view plain copy

  1. <beans>    
  2. <context:annotation-config/>    
  3. <!-- bean definitions go here -->    
  4. </beans>    

 <context:annotation-config/>标签配置完成以后,就可以用注解的方式在Spring中向属性、方法和构造方法中自动装配变量。

下面是几种比较重要的注解类型:

  1. @Required:该注解应用于设值方法。
  2. @Autowired:该注解应用于有值设值方法、非设值方法、构造方法和变量。
  3. @Qualifier:该注解和@Autowired注解搭配使用,用于消除特定bean自动装配的歧义。
  4. JSR-250 Annotations:Spring支持基于JSR-250 注解的以下注解,@Resource、@PostConstruct 和 @PreDestroy。

202. 10、请解释Spring Bean的生命周期?

Spring Bean的生命周期简单易懂。在一个bean实例被初始化时,需要执行一系列的初始化操作以达到可用的状态。同样的,当一个bean不在被调用时需要进行相关的析构操作,并从bean容器中移除。

Spring bean factory 负责管理在spring容器中被创建的bean的生命周期。Bean的生命周期由两组回调(call back)方法组成。

  1. 初始化之后调用的回调方法。
  2. 销毁之前调用的回调方法。

Spring框架提供了以下四种方式来管理bean的生命周期事件:

  • InitializingBean和DisposableBean回调接口
  • 针对特殊行为的其他Aware接口
  • Bean配置文件中的Custom init()方法和destroy()方法
  • @PostConstruct和@PreDestroy注解方式

使用customInit() customDestroy()方法管理bean生命周期的代码样例如下:

[html] view plain copy

  1. <beans>    
  2. <bean id="demoBean" class="com.somnus.task.DemoBean" init-method="customInit" destroy-method="customDestroy"></bean>    
  3. </beans>    

203. 11、Spring Bean的作用域之间有什么区别?

Spring容器中的bean可以分为5个范围。所有范围的名称都是自说明的,但是为了避免混淆,还是让我们来解释一下:

  1. singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
  2. prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
  3. request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
  4. Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
  5. global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。

全局作用域与Servlet中的session作用域效果相同。

204. 12、什么是Spring inner beans?

Spring框架中,无论何时bean被使用时,当仅被调用了一个属性。一个明智的做法是将这个bean声明为内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现。

比如,在我们的应用程序中,一个Customer类引用了一个Person类,我们的要做的是创建一个Person的实例,然后在Customer内部使用。

内部bean的声明方式如下:

 

205. 13、Spring框架中的单例Beans是线程安全的么?

Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。
最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。

206. 14、请举例说明如何在Spring中注入一个Java Collection?

Spring提供了以下四种集合类的配置元素:

  • <list> :   该标签用来装配可重复的list值。
  • <set> :    该标签用来装配没有重复的set值。
  • <map>:   该标签可用来注入键和值可以为任何类型的键值对。
  • <props> : 该标签支持注入键和值都是字符串类型的键值对。

下面看一下具体的例子:

 

207. 15、如何向Spring Bean中注入一个Java.util.Properties?

第一种方法是使用如下面代码所示的<props> 标签:

也可用”util:”命名空间来从properties文件中创建出一个propertiesbean,然后利用setter方法注入bean的引用。

208. 16、请解释Spring Bean的自动装配?

Spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring容器还可以自动装配合作关系bean之间的关联关系。这意味着Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上。

下面的XML配置文件表明了如何根据名称将一个bean设置为自动装配:

[html] view plain copy

  1. <bean id="employeeDAO" class="com.howtodoinjava.EmployeeDAOImpl" autowire="byName" />   

除了bean配置文件中提供的自动装配模式,还可以使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在按照如下的配置方式在Spring配置文件进行配置才可以使用。

[html] view plain copy

  1. <context:annotation-config />  

也可以通过在配置文件中配置AutowiredAnnotationBeanPostProcessor 达到相同的效果。

[html] view plain copy

  1. <bean class ="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>    

配置好以后就可以使用@Autowired来标注了。

 

209. 17、请解释自动装配模式的区别?

Spring框架中共有5种自动装配,让我们逐一分析。

  1. no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系。
  2. byName:该选项可以根据bean名称设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的名称自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
  3. byType:该选项可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
  4. constructor:造器的自动装配和byType模式类似,但是仅仅适用于与有构造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
  5. autodetect:该模式自动探测使用构造器自动装配或者byType自动装配。首先,首先会尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在bean内部没有找到相应的构造器或者是无参构造器,容器就会自动选择byTpe的自动装配方式。

210. 18、如何开启基于注解的自动装配?

要使用 @Autowired,需要注册 AutowiredAnnotationBeanPostProcessor,可以有以下两种方式来实现:

1、引入配置文件中的<bean>下引入 <context:annotation-config>

 

211. 19、请举例解释@Required注解?

在产品级别的应用中,IoC容器可能声明了数十万了bean,bean与bean之间有着复杂的依赖关系。设值注解方法的短板之一就是验证所有的属性是否被注解是一项十分困难的操作。可以通过在<bean>中设置“dependency-check”来解决这个问题。

在应用程序的生命周期中,你可能不大愿意花时间在验证所有bean的属性是否按照上下文文件正确配置。或者你宁可验证某个bean的特定属性是否被正确的设置。即使是用“dependency-check”属性也不能很好的解决这个问题,在这种情况下,你需要使用@Required 注解。

需要用如下的方式使用来标明bean的设值方法。

RequiredAnnotationBeanPostProcessorSpring中的后置处理用来验证被@Required 注解的bean属性是否被正确的设置了。在使用RequiredAnnotationBeanPostProcesso来验证bean属性之前,首先要在IoC容器中对其进行注册:

[html] view plain copy

  1. <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />    

但是如果没有属性被用 @Required 注解过的话,后置处理器会抛出一个BeanInitializationException 异常。

212. 20、请举例解释@Autowired注解?

@Autowired注解对自动装配何时何处被实现提供了更多细粒度的控制。@Autowired注解可以像@Required注解、构造器一样被用于在bean的设值方法上自动装配bean的属性,一个参数或者带有任意名称或带有多个参数的方法。

比如,可以在设值方法上使用@Autowired注解来替代配置文件中的 <property>元素。当Spring容器在setter方法上找到@Autowired注解时,会尝试用byType 自动装配。

当然我们也可以在构造方法上使用@Autowired 注解。带有@Autowired 注解的构造方法意味着在创建一个bean时将会被自动装配,即便在配置文件中使用<constructor-arg> 元素。

 

213. 21、请举例说明@Qualifier注解?

@Qualifier注解意味着可以在被标注bean的字段上可以自动装配。Qualifier注解可以用来取消Spring不能取消的bean应用。

下面的示例将会在Customer的person属性中自动装配person的值。

[java] view plain copy

  1. public class Customer{    
  2. @Autowired    
  3. private Person person;    
  4. }   

下面我们要在配置文件中来配置Person类。

Spring会知道要自动装配哪个person bean么?不会的,但是运行上面的示例时,会抛出下面的异常:

[html] view plain copy

  1. Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:    
  2. No unique bean of type [com.howtodoinjava.common.Person] is defined:    
  3. expected single matching bean but found 2: [personA, personB]    

要解决上面的问题,需要使用 @Quanlifier注解来告诉Spring容器要装配哪个bean:

[java] view plain copy

  1. public class Customer{    
  2. @Autowired    
  3. @Qualifier("personA")    
  4. private Person person;    
  5. }  

 

214. 22、构造方法注入和设值注入有什么区别?

请注意以下明显的区别:

  1. 在设值注入方法支持大部分的依赖注入,如果我们仅需要注入int、string和long型的变量,我们不要用设值的方法注入。对于基本类型,如果我们没有注入的话,可以为基本类型设置默认值。在构造方法注入不支持大部分的依赖注入,因为在调用构造方法中必须传入正确的构造参数,否则的话为报错。
  2. 设值注入不会重写构造方法的值。如果我们对同一个变量同时使用了构造方法注入又使用了设置方法注入的话,那么构造方法将不能覆盖由设值方法注入的值。很明显,因为构造方法尽在对象被创建时调用。
  3. 在使用设值注入时有可能还不能保证某种依赖是否已经被注入,也就是说这时对象的依赖关系有可能是不完整的。而在另一种情况下,构造器注入则不允许生成依赖关系不完整的对象。
  4. 在设值注入时如果对象A和对象B互相依赖,在创建对象A时Spring会抛出sObjectCurrentlyInCreationException异常,因为在B对象被创建之前A对象是不能被创建的,反之亦然。所以Spring用设值注入的方法解决了循环依赖的问题,因对象的设值方法是在对象被创建之前被调用的。

215. 23、Spring框架中有哪些不同类型的事件?

Spring的ApplicationContext 提供了支持事件和代码中监听器的功能。

我们可以创建bean用来监听在ApplicationContext 中发布的事件。ApplicationEvent类和在ApplicationContext接口中处理的事件,如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

 

Spring 提供了以下5中标准的事件:

  1. 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  5. 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

除了上面介绍的事件以外,还可以通过扩展ApplicationEvent 类来开发自定义的事件。

为了监听这个事件,还需要创建一个监听器:


之后通过applicationContext接口的publishEvent()方法来发布自定义事件。

[java] view plain copy

  1. CustomApplicationEvent customEvent = new CustomApplicationEvent(applicationContext, "Test message");    
  2. applicationContext.publishEvent(customEvent);    

 

216. 24、FileSystemResource和ClassPathResource有何区别?

在FileSystemResource 中需要给出spring-config.xml文件在你项目中的相对路径或者绝对路径。在ClassPathResourcespring会在ClassPath中自动搜寻配置文件,所以要把ClassPathResource 文件放在ClassPath下。

如果将spring-config.xml保存在了src文件夹下的话,只需给出配置文件的名称即可,因为src文件夹是默认。

简而言之,ClassPathResource在环境变量中读取配置文件,FileSystemResource在配置文件中读取配置文件。

217. 25、Spring 框架中都用到了哪些设计模式?

Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:

  • 代理模式—在AOP和remoting中被用的比较多。
  • 单例模式—在spring配置文件中定义的bean默认为单例模式。
  • 模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
  • 前端控制器—Spring提供了DispatcherServlet来对请求进行分发。
  • 视图帮助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。
  • 依赖注入—贯穿于BeanFactory / ApplicationContext接口的核心理念。
  • 工厂模式—BeanFactory用来创建对象的实例。

转至:http://blog.csdn.net/lovesomnus/article/details/46470255

 

posted @ 2018-07-03 16:54  KING。。。  阅读(704)  评论(0编辑  收藏  举报