一、String,StringBuffer, StringBuilder 的区别是什么?String为什么是不可变的?
String是字符串常量,所指向的内容是不可变的,因为String 在JDK中声明为final类。StringBuffer和StringBuilder是变量。
StringBuffer是线程安全的,StringBuilder是线程不安全的,如果只用在单线程中,StringBuilder的效率高于StringBuffer。
二、Vector,ArrayList, LinkedList的区别是什么?
Vector和ArrayList的底层是基于数组形式的,而LinkedList底层是基于链表的,前两者适合查找,不适合特定位置的插入和删除,LinkedList适合插入,不适合查找。Vector是线程安全同步的,而ArrayList、LinkedList是线程不安全不同步的。
三、HashTable, HashMap,TreeMap区别?
HashTable是线程安全同步的,不允许有键值有null的出现。HashMap是线程不安全不同步的,允许有null的出现,注意会问到hashcode的作用(两个不同对象的hashcode可能是一样的,而两个 对象hashcode不同,则两个对象肯定不同,所以插入hashmap先用hashcode进行对比,如果相同再用equal进行判断)。TreeMap是默认按照键的升序进行排列的。
四、Tomcat,Apache,JBoss的区别?
Tomcat是Web服务器,Apache是Http服务器,JBoss是应用服务器。Tomcat可以解析动态的jsp页面,还可以充当servlet容器。Apache可以解析静态的html页面。
五、Session, Cookie区别
Session保存的是服务器端信息,Cookie保存的是浏览器端信息。用户可以通过浏览器设置是否保存Cookie,但不能设置Session,Cookie和Session都是用来跟踪用户信息的,Cookie保存的是字符串,Session保存的是对象。
六、Servlet的生命周期
init()
service()负责处理来自客户端的请求
destroy()销毁servlet对象,并释放其占用资源
七、Statement与PreparedStatement的区别,什么是SQL注入,如何防止SQL注入
PreparedStatement支持动态参数的绑定,而Statement不支持
PreparedStatemen支持预编译,而Statement不支持
PreparedStatement可以防止恶意的SQL注入
所谓的SQL注入是指通过sql语句的拼接达到无参数查询数据库的目的,PreparedStatement支持在服务器端接收参数后,自动验证,而Statement需要手工验证。
SQL注入原理是把SQL命令插入到WEB表单提交或输入域名或页面请求的查询字符串,最终欺骗服务器达到恶意SQL命令。
预防措施:
1)不要相信用户的输入,需要对其进行验证
2)不要动态拼接SQL语句,可以使用参数化的sql
3)不要把机密信息直接存放,加密或者hash掉密码和敏感的信息
八、sendRedirect, foward区别
forward是服务器端控制页面转向,在浏览器端并不会转向的地址。而sendRedirect是完全的跳转,浏览器中显示转向的地址
九、谈谈hibernate的理解,一级和二级缓存的作用,在项目中Hibernate都是怎么使用缓存的。
临时状态:不在session缓存中,数据库中没有对象记录
持久化对象:在session缓存中,数据库中没有对象记录,session在特定时候保持两者同步,具有唯一OID
游离对象:由持久化对象转换而来,数据库中没有对象记录,不再处于session缓存中,调用session的evict(),close()方法可以删除session中的对象
一级缓存就是session,是hibernate自带的,二级缓存是sessionFactory。
Hibernate二级缓存的策略一般是:
1)条件查询的时候,SQL语句SELECT * FROM table_name WHERE查询出所有的数据对象
2)把获取的所有对象根据ID放入到二级缓存中
3)当Hibernate通过ID访问对象时,先从一级缓存中找,再从二级缓存中找,如果都不存在,则去数据库中找
4)删除、更新、添加数据同步更新缓存
十、反射讲一讲,主要是概念,都在哪需要反射机制,反射的性能,如何优化
反射机制是指在运行过程中,对于任意一个类,能获取这个类的所有属性和方法。对于任意一个对象,能够调用它的任意一个方法。这种动态获取信息及动态调用对象的方法称为反射。
反射提供的功能:
1.在运行过程中判断一个对象所属的类
2.构造任意一个类的对象
3.判断一个类所具有的属性和方法
4.调用任意对象的方法
5.动态代理
十一、对Spring的理解,项目中都用什么?怎么用的?对IOC、和AOP的理解及实现原理
IOC:是指组件之间的依赖关系是由容器在运行过程中决定的,依赖注入的方式有两种,通过构造函数和set方法
AOP:面向切面编程,例如把核心业务模块中重复的代码重构封装起来,比如配置日置。在Spring Aop中,存在下面几种通知
1)前置通知
2)后置通知
3)异常通知
4)环绕通知:拦截对目标方法对象调用
切面:一个关注点的模块化
连接点:程序执行过程中明确的点,如方法的调用和特定异常的抛出
通知:在特定的连接点,AOP框架执行的动作。例如前置通知、后置通知等
切入点:指定一个通知将被引发的一系列的连接点的集合。
十二、描述struts的工作流程
- 在web应用启动时,会初始化并加载ActionServlet对象,ActionServlet会读取struts-config.xml配置文件。
- ActionServlet会根据客户端发出的请求,检索与用户请求相匹配的ActionMapping对象,如果不存在,则返回一个无效路径信息。
3)如果不存在ActionForm实例,则会创建一个该对象,并把表单信息保存到其中
4)如果配置了验证表单信息,则会调用ActionForm的validate 方法,如果返回null或者没有错误信息的ActionErrors,则表单验证成功
5.)ActionServlet会根据ActionMapping中包含的映射信息决定请求转发给哪个Action,然后调用该Action的execute()方法。
6)Action的execute()方法会返回一个ActionForward对象,ActionServlet再把请求转发给ActionForward对象指向的JSP页面。
7)ActionForward对象指向的JSP页面生成动态网页,返回给客户。
十三、JVM垃圾回收实现原理。垃圾回收的线程优先级。
垃圾回收器主要有三种:串行收集器、并行收集器、并发收集器
1)串行收集器,使用单线程处理所有垃圾回收,效率比较高,但无法发挥多核处理器的优势,适合小数据量,比如100M大小
2)并行收集器,对年轻代进行并行回收,减少垃圾回收的时间,一般在多线程多核处理器上使用。
3)并发收集器,大部分时间都能保证程序是并发进行的,垃圾回收只占用很少时间,适合对响应要求比较高的中、大规模应用。
年轻代:分三个区,一个Eden区,两个Survivor区。当Eden区满的时候,会存放到一个Survivor区,当这个Survivor区也满了,会存放到第二个Survivor区,如果这个区也满了,会将从第一个Survivor区复制过来且存活的对象放到年老代中。
年老代:主要存放着从年轻代转化过来的对象,一般来说是生命周期比较长的对象
持久代:一般是静态资源,比如java类和方法等,一般不会影响垃圾回收。但是一些应用可能会动态生成或者调用一些class,比如Hibernate,因此需要设置比较大的持久代空间来存放这些运行过程中增加的类。
十四、TCP/IP(三次握手,四次挥手)
有6种标示:
SYN(synchronous建立联机)
ACK(acknowledgement 确认)
PSH(push传送)
FIN(finish结束)
RST(reset重置)
URG(urgent紧急)
Sequence number(顺序号码) Acknowledge number(确认号码)
三次握手
1)第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
2)第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包
3)主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
四次挥手
1) 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送
2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A
4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。
客户端:CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服务器:CLOSED->LISTEN->SYN收到 ->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
-1. CLOSED: 表示初始状态。
-2. LISTEN: 表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
-3. SYN_RCVD: 这个状态表示接受到了SYN报文
SYN_SENT状态表示客户端已发送SYN报文。
-4. ESTABLISHED:表示连接已经建立了。
-5. FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态
-6. FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
-7. TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。
-8. CLOSING: 那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
-9. CLOSE_WAIT:当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
-10. LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文
十五、数据库(事务隔离级别,脏读,不可重复读,幻读)
脏读:一个事务读取了另一个事务未提交的数据
不可重复读:比如事务A 读取了数据,但是事务B马上提交了数据,此时A再读数据的话,就出现了不可重复数据
幻读:幻读和不可重复读类似,不可重复是数据发生了改变,而幻读是数据的记录数量发生了改变
未提交读:允许其他事务看到未提交的数据,会出现脏读,最低级别
已提交读:被读取的数据可以被其他事务修改,就是说事务在读取数据的时候获取读锁,但是读完之后立即释放(不需要等到事务提交之后),而写锁是在事务提交之后释放。释放完读锁后,数据就可能被其他事务修改,产生不可重复读
可重复读:可以避免脏读、不可重复读,但不能避免幻读
串行化:都可以避免
十六、Linux操作系统
ls mkdir/rmdir cd pwd cp mv ps(查看系统目前进程) kill man(在线帮助命令) vi(vi编辑器命令) gedit gcc(编译器命令) tar chmod(修改权限) [u所属用户 g所属组 o其他用户 a所有用户]
十七、HashMap
HashMap底层就是个数组结构,数组中的每一项又是一个链表,Entry<K,V>就是数组中的元素,他持有指向下一个元素的引用,就构成了链表。
HashMap底层把key-value当成一个整体对象来看,即为Entry对象。HashMap底层用一个Entry[]保存所有key-value。当需要存储一个元素时,先根据hash算法(一般的hash算法就是hashcode对数组的长度取模,hashmap这里使用一个高效的方法,h&(table.length-1,length总为2的n次方,因此该运算等价于对length取模))确定其在数组中的存储位置,然后通过equals方法确定其在数组位置上的链表中的存储位置(如果数组该位置已经存放了元素,那么这个位置上的元素将以链表的形式存放,新加入的在链表头,后加入的在链表尾,如果数组该位置上没有元素,则直接放在数组该位置)。当需要从map中获取一个元素时,先通过hash算法确定其在数组中的存储位置,再通过equals方法从该位置的链表中取出该Entry。
十八、 HashCode和equal方法
equals反映的是对象或变量具体的值,即两个对象包含的值(可能是对象的引用,可能是值类型的值)
hashcode是对象或者变量通过hash算法计算出来的值。
作用:
1⃣️hashcode是用来提高Map的搜索效率,通过hashcode计算出放在哪个桶里,然后通过equals方法找出是否有重复的元素
2⃣️想要正确的实现Map查找元素必须要满足以下两个条件:
1)两个元素equals方法为true,则hashcode必定为true
2)两个元素的hashcode为false,则equals方法必定为false
如果两个元素的hashcode为true,再需要通过equals方法进行比较
十九、进程线程,安全问题
1)进程和线程的区别?
进程是一个独立的运行环境,可以看成一个程序或应用,线程是进程中的一个任务。
2)用户线程和守护线程的区别?
在Java 程序中创建一个线程就是用户线程,守护线程是java后台提供的一种通用服务的线程,例如垃圾回收机制。守护线程不会阻止JVM的停止,只要还有一个用户线程还在,所有的守护线程都工作,用户线程停止工作,程序退出,则守护线程也退出了。
3)如何创建一个线程?
一种是实现Runnable接口,把它传递给Thread的构造函数,创建Thread对象
- ThreadLocal的作用?
ThreadLocal并不是用来解决同步问题的,它只允许我们创建一个只能被同一线程访问的变量。如果一段代码含有ThreadLocal,即使有两个线程访问,也无法访问到对方的ThreadLocal。例如JDBC中获取Connection,把它与ThreadLocal进行绑定,那么就不需要每次创建Connection,然是直接从ThreadLocal中读出来。 - CAS乐观锁
乐观锁的核心是比较并交换,设计3个操作值:内存值、预期值、新值,当且仅当预期值和内存值想等时才会把内存值更新为新值。
二十、volatile
禁止指令重排 可见线
会把volatile变量,会把线程所对应的该变量内存刷新到主内存中。
1)第二个volatile变量写,第一个无论是什么操作,都不能进行重排序
2)第一个volatile变量读,第二个无论是什么操作,都不能进行重排序
3)第一个是volatile写,第二个是volatile读
这个三个规则构成了两个volatile变量操作不能进行重排序
1、 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
2、 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令。
通俗得讲,volatile变量在每次被线程访问时,都强迫从主内存中读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存。这样任何时刻,不同的线程总是能够看到该变量的最新值。
二十一、多线程wait,notify, synchronized
wait方法使得当前线程等待,必须等待另一个线程调用notify或notifyAll方法,当前线程必须拥有该对象的锁,这意味着wait方法必须放在synchronized代码块中
notify方法会唤醒一个等待当前对象锁的线程,notify应该是被拥有对象锁的线程所调用,同样意味着notify方法也需要放在synchronized代码块中。
二十二、用户线程与守护线程
守护线程就是程序在运行过程中后台提供的一种通用服务的线程,一般来说JVM 中有任意一个用户线程存在,那么所有守护线程全部工作,守护线程一个典型的案例就是垃圾回收机制。
二十三、KMP算法
KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,只要继续把它向后移和移动匹配词就可以,这样就提高了效率。可以针对搜索词,算出一张部分匹配表。通过查表查到最后一个匹配字符对应的部分匹配值,并利用以下公式计算匹配词向后移动的位数:
移动位数 = 已匹配的字符数 - 对应的部分匹配值
二十四、Java NIO和IO
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道
二十五、SQL优化问题
1、 数据库逻辑设计的规范化
1)没有重复的组或多值的列,这是数据库设计的最低要求
2)每个非关键字段必须依赖于主关键字,不能依赖于一个组合式主关键字的某些组成部分。消除部分依赖,大部分情况下,数据库设计都应该达到第二范式。
3)一个非关键字段不能依赖于另一个非关键字段。
2、 合理的冗余
1)
3、 主键的设计
主键是必要的,SQL SERVER的主键同时是一个唯一索引,而且在实际应用中,我们往往选择最小的键组合作为主键,所以主键往往适合作为表的聚集索引。聚集索引对查询的影响是比较大的,这个在下面索引的叙述。
4、 外键的设计
外键是最高效的一致性维护方法,数据库的一致性要求,依次可以用外键、CHECK约束、规则约束、触发器、客户端程序,一般认为,离数据越近的方法效率越高。
5、 字段的设计
1)数据类型尽量用数字型,数字型的比较比字符型的快很多。
2) 数据类型尽量小,这里的尽量小是指在满足可以预见的未来需求的前提下的。
3) 尽量不要允许NULL,除非必要,可以用NOT NULL+DEFAULT代替。
4)少用TEXT和IMAGE,二进制字段的读写是比较慢的,而且,读取的方法也不多,大部分情况下最好不用。
5) 自增字段要慎用,不利于数据迁移
注意事务和锁
A、事务操作过程要尽量小,能拆分的事务要拆分开来。
B、 事务操作过程不应该有交互,因为交互等待的时候,事务并未结束,可能锁定了很多资源
C、 事务操作过程要按同一顺序访问对象。
D、提高事务中每个语句的效率,利用索引和其他方法提高每个语句的效率可以有效地减少整个事务的执行时间。
二十六 ReentrantLock
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。
reentrant 锁意味着什么呢?
简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。
相同:ReentrantLock提供了synchronized类似的功能和内存语义。
不同:
(1)ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。
(2)ReentrantLock 的性能比synchronized会好点。
(3)ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。
Condition条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。而且多个Condition需要绑定到同一锁上。前面的Lock中提到,获取一个条件变量的方法是Lock.newCondition()。
死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。在这里我已写好一个简单的程序,它将会引起死锁方案然后我们就会明白如何分析它。
二十七 happens before
什么是happens-before关系? 这个关系其实就是一个保证而已,那么保证什么呢?它保证一条语句对内存的写操作对另一条语句是可见的。换句话说,如果写操作A和读操作B存在happens-before这种关系,那么写操作在结束以后都操作才能开始。
下面是Java内存模型中的八条可保证happen—before的规则,它们无需任何同步器协助就已经存在,可以在编码中直接使用。
1)程序次序规则:在一个单独的线程中,按照程序代码的执行流顺序,(时间上)先执行的操作happen—before(时间上)后执行的操作。
2)管理锁定规则:一个unlock操作happen—before后面(时间上的先后顺序,下同)对同一个锁的lock操作。
3)volatile变量规则:对一个volatile变量的写操作happen—before后面对该变量的读操作。
4)线程启动规则:Thread对象的start()方法happen—before此线程的每一个动作。
5)线程终止规则:线程的所有操作都happen—before对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
6)线程中断规则:对线程interrupt()方法的调用happen—before发生于被中断线程的代码检测到中断时事件的发生。
7)对象终结规则:一个对象的初始化完成(构造函数执行结束)happen—before它的finalize()方法的开始。
8)传递性:如果操作A happen—before操作B,操作B happen—before操作C,那么可以得出A happen—before操作C。
这里的八个规则除了第三个以外都容易理解。所以专门讲一下volatile变量规则。
1、 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
2、 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令。
通俗得讲,volatile变量在每次被线程访问时,都强迫从主内存中读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存。这样任何时刻,不同的线程总是能够看到该变量的最新值。
二十八 java所有单例模式
1、饿汉式(静态常量)
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
2、饿汉式(静态代码块)[可用]
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {}
public Singleton getInstance() {
return instance;
}
}
3、懒汉式(线程不安全)[不可用]
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
4、懒汉式(线程安全,同步方法)[不推荐用]
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
5、懒汉式(线程安全,同步代码块)[不可用]
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
6、双重检查[推荐用]
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
7、静态内部类[推荐用]
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
二十九 java线程池
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
corePoolSize(线程池的基本大小)
maximumPoolSize(线程池最大大小)
runnableTaskQueue(任务队列)
1⃣️ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)
2⃣️LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。
3⃣️SynchronousQueue:一个不存储元素的阻塞队列
RejectedExecutionHandler(饱和策略)
1⃣️CallerRunsPolicy:只用调用者所在线程来运行任务。
2⃣️DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
3⃣️DiscardPolicy:不处理,丢弃掉。
网友评论