Java热门面试题基础篇

基础

Java基础&Javaweb

1.JDK、JRE、JVM区别

JDK是 Java 语言的软件开发工具包(SDK)。在JDK的安装目录下有一个jre目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib合起来就称为jre。

2.String、StringBuffer、StringBuilder区别

可变性

简单的来说:String 类中使用 final 关键字字符数组保存字符串, private final char value[] ,所以 String 对象是不可变的。而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串 char[]value 但是没有用 final 关键字修饰, 所以这两种对象都是可变的。

线程安全性

String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义 了一些字符串的基本操作,expandCapacity.append.insert.indexOf 等公共 方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的

性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然

后将指针指向新的 String 对象。 StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新 的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

3.==和equals方法的区别

== : 它的作用是判断两个对象的地址是不是相等。即: 判断两个对象是不 是同一个对象。(基本数据类型比较的是值,引用数据类型比较的是内存 地址)。equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况,

如下:

情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两 个对象时,等价于通过“==”比较这两个对象。

情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法 来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象 相等)。

4.Integer和int的区别

1.Integer是int的包装类,int则是java的一种基本数据类型

2.Integer变量必须实例化后才能使用,而int变量不需要

3.Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 。

4.Integer的默认值是null,int的默认值是0

5.抽象类和接口的区别

  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始

接口方法可以有默认实现),抽象类可以有非抽象的方法。

  1. 接口中的实例变量默认是 final 类型的,而抽象类中则不一定。
  2. 一个类可以实现多个接口,但最多只能实现一个抽象类。
  3. 一个类实现接口的话要实现接口的所有方法,而抽象类不一定。
  4. 接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口

的对象

从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的

抽象,是一种行为的规范。

备注: 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实

现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认

方法,必须重写,不然会报错。

6.创建对象有几种方式

1.用new语句创建对象,这是最常用的创建对象的方式。

2.运用反射手段,调用Java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。

3.调用对象的clone()方法。

4.运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法.

7.list和set的区别

List和Set都是接口。他们各自有自己的实现类,有无顺序的实现类,也有有顺序的实现类。 最大的不同就是List是可以重复的。而Set是不能重复的。 List适合经常追加数据,插入,删除数据。但随即取数效率比较低。 Set适合经常地随即储存,插入,删除。但是在遍历时效率比较低

8.ArrayList和linklist 的区别。

Arraylist:底层是基于动态数组,根据下表随机访问数组元素的效率高,向数组尾部添加元素的效率高;但是,删除数组中的数据以及向数组中间添加数据效率低,因为需要移动数组。

Linkedlist基于链表的动态数组,数据添加删除效率高,只需要改变指针指向即可,但是访问数据的平均效率低,需要对链表进行遍历。

对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据

9.类的权限修饰符的作用

Java中四种权限修饰符:public(公共的)、protected(受保护的)、default(默认的)、private(私有的) 对于class的权限修饰只可以用public和default(默认的)。

状态修饰符:static(静态的),final(最终的)

抽象修饰符:abstract(抽象的)

10.HashMap和Hashtable的区别

HashMap 是线程不安全的,HashMap 是一个接口,是 Map 的一个子接 口,是将键映射到值得对象,不允许键值重复,允许空键和空值;由于非线程安 全,HashMap 的效率要较 HashTable 的效率高一些.

HashTable 是线程安全的一个集合,不允许 null 值作为一个 key 值或者 Value 值; HashTable 是 sychronize,多个线程访问时不需要自己为它的方法实现同 步,而 HashMap 在被多个线程访问的时候需要自己为它的方法实现同步;

11.举例最常用的五个运行时异常

(1)java.lang.NullPointerException 【空指针异常】

(2)java.lang.ClassNotFoundException 【类找不到异常】

(3)java.lang.NumberFormatException 【数字格式化异常】

(4)java.lang.IndexOutOfBoundsException 【数组角标越界异常】或 【数组索引越界异常】

(5)java.lang.IllegalArgumentException 【非法参数异常】

(6)java.lang.ClassCastException 【类型转换异常】

(7)java.lang.NoClassDefFoundException 【类未定义异常】

(8)SQLException 操作数据库异常 【SQL异常】

(9)java.io.IOException 【IO异常】

(10)java.lang.NoSuchMethodException 【没有匹配方法异常】

12.转发与重定向的区别

本质区别:转发是服务器行为,重定向是客户端行为。

重定向特点:两次请求,浏览器地址发生变化,可以访问自己 web 之外的

资源,传输的数据会丢失。

请求转发特点:一次请求,浏览器地址不变,访问的是自己本身的 web 资

源,传输的数据不会丢失。

13.cookie和session的区别

Cookie 是 web 服务器发送给浏览器的一块信息,浏览器会在本地一个文

件中给每个 web 服务器存储 cookie。以后浏览器再给特定的 web 服务器发

送请求时,同时会发送所有为该服务器存储的 cookie。

Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户 会话所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储 在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。

Cookie 和 session 的不同点:

1.无论客户端做怎样的设置,session 都能够正常工作。当客户端禁用

cookie 时将无法使用 cookie。

2.在存储的数据量方面:session 能够存储任意的 java 对象,cookie 只

能存储 String 类型的对象。

14.ThreadLocal 的原理

ThreadLocal的作用就是:线程安全。

ThreadLocal的本质就是一个内部的静态的map,key是当前线程的句柄,value是需要保持的值。由于是内部静态map,不提供遍历和查询的接口,每个线程只能获取自己线程的value。这样,就线程安全了,又提供了数据共享的能力,perfect。ThreadLocal是线程本地存储,在每个线程中都创建了一个ThreadLocalMap对象,每个线程可以访问自己内部ThreadLocalMap对象内的value。

15.jdk1.7和jdk1.8的区别是什么?

jdk 1.8 和1.7有什么区别

一、接口的默认方法

Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法。

二、Lambda 表达式

在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:

16.Hashmap底层实现原理

HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到 它的值.因而具有很快的访问速度,但是遍历顺序却不确定的.HashMap 最多只允 许一条记录的键为 null,允许多条记录的值为 null。 HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致。 如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者使用ConcurrentHashMap,HashTable.

17.Hashmap和conCurrentHashMap的区别

性能:ConcurrentHashMap(线程安全) > HashMap > HashTable(线程安全)

区别对比一(HashMap 和 HashTable 区别):

1、HashMap 是非线程安全的,HashTable 是线程安全的。

2、HashMap 的键和值都允许有 null 值存在,而 HashTable 则不行。

3、因为线程安全的问题,HashMap 效率比 HashTable 的要高。

4、Hashtable 是同步的,而 HashMap 不是。因此,HashMap 更适合于单线 程环境,而 Hashtable 适合于多线程环境。一般现在不建议用 HashTable, ① 是 HashTable 是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下, 现在也有同步的 ConcurrentHashMap 替代,没有必要因为是多线程而用 HashTable。

区别对比二(HashTable 和 ConcurrentHashMap 区别):

HashTable 使用的是 Synchronized 关键字修饰,ConcurrentHashMap 是 使用了锁分段技术来保证线程安全的。

Hashtable 中采用的锁机制是一次锁住整个 hash 表,从而在同一时刻只能由一 个线程对其进行操作;而 ConcurrentHashMap 中则是一次锁住一个桶。 ConcurrentHashMap 默认将 hash 表分为 16 个桶,诸如 get、put、remove 等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能 同时有 16 个写线程执行,并发性能的提升是显而易见的

18.servlet的生命周期?

Servlet 的执行流程也就是 servlet 的生命周期,当服务器启动的时候生 命周期开始,然后通过 init()《启动顺序根据 web.xml 里的 startup-on-load 来 确定加载顺序》方法初始化 servlet,再根据不同请求调用 doGet 或 doPost 方 法,最后再通过 destroy()方法进行销毁。

19.字节流和字符流的区别

每次读写的字节数不同;

字符流是块读写,字节流是字节读写;

字符流带有缓存,字节流没有。

20.jsp的四大域对象及作用于范围

  1. pageContext
    当前jsp页面范围内有效
  2. request
    一次请求内有效
  3. session
    一次会话范围内有效,也就是打开浏览器访问服务器到关闭浏览器
  4. application
    整个Web工程内有效

21.String常用的方法有哪些?

1、indexOf(“字符”):查询指定的字符串是否存在,返回的是字符串的位置,不存在返回-1;

2、CharAt(值):拿到指定位置的字符;

3、trim():去除字符串两端的空格;

4、split():分割字符串,返回分割后的字符串数组;

5、length():返回字符串的长度;

6、substring(int begIndex,int endIndex):截取字符串;

7、equals():比较两个字符串是否相等;

8、toLowerCase():将字符串转换为小写字母;

9、toUpperCase():将字符串转换为大写字母;

10、equalsIgnoreCase(String):忽略大小写比较两个值是否相等;

11、replace():替换字符串,把第一次出现的位置替换掉;

22.JVM加载类的过程。

JVM加载类的过程

23.Java中四中引用类型分别是什么,他们有什么区别?<扩展>

强引用

在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到JVM也不会回收。因此强引用是造成Java内存泄漏的主要原因之一。

软引用

软引用需要用SoftReference类来实现,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。

弱引用

弱引用需要用WeakReference类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,总会回收该对象占用的内存。

虚引用

虚引用需要PhantomReference类来实现,它不能单独使用,必须和引用队列联合使用。虚引用的主要作用是跟踪对象被垃圾回收的状态。

24.jdbc的执行流程简单聊聊?

第一步:Class.forName()加载数据库连接驱动;

第二步:DriverManager.getConnection()获取数据连接对象;

第 三 步 : 根 据 SQL 获 取 sql 会 话 对 象 , 有 2 种 方 式Statement.PreparedStatement ;

第四步:执行 SQL 处理结果集,执行 SQL 前如果有参数值就设置参数值 setXXX();

第五步:关闭结果集.关闭会话.关闭连接。

25.tcp的三次握手和四次挥手?

三次握手

为了准确无误的将数据发送到指定IP处,TCP协议采用了三次握手的策略,如下步骤所示:

1、客户端采用TCP协议将带有SYN标志的数据包发送给服务器,等待服务器的确认。

2、服务器端在收到SYN的数据包后,必须确认SYN,即自己发送的ACK标志,同时,自己也将会向客户端发送一个SYN标志。

3、客户端在接收到服务器短的SYN+ACK包后,自己会向服务器发送ACK包,完成三次握手。那么客户端和服务器正式建立了连接,开始传输数据。

三次握手的图如下所示:

四次挥手

四次挥手是用来断开服务器和客户端之间的通信的,之所以要断开连接,是因为TCP/IP 协议是要占用端口号的,而计算机的端口却是有限的,不进行断开的话,势必会造成计算机资源的浪费。

1、在整个通信的过程中,谁先发起请求,谁就是客户端。

当客户端的数据传输到尾部时,客户端向服务器发送带有FIN标志的数据包,使其明白自己准备断开通信了。

2、因为TCP的通信是使用全双工通信的WebSocket,所以在断开连接的时候也应该是双向的;当服务器收到带有FIN标志的数据包时,其必不会直接发送FIN标志断开通信的请求,而是先发送一个带有ACK标志的应答信息,使客户端明白服务器还有数据要进行发送。

3、当 服务器的数据发送完成后,向客户端发送带有FIN标志的数据包,通知客户端断开连接。

4、这一次挥手是我觉得四次挥手中设计的最巧妙的一次。

当客户端收到FIN后,担心网络上某些不可控制的因素导致服务器不知道他要断开连接,会发送ACK进行确认,同时把自己设置成TIME_WAIT状态并启动定时器,在TCP的定时器到达后客户端并没有接收到请求,会重新发送;当服务器收到请求后就断开连接;当客户端等待2MLS(两倍报文最大生存时间)后,没有收到请求重传的请求后,客户端这边就断开连接,整个TCP通信就结束了。

注:三次握手为什么不能改成两次握手?

解:三次握手中的每一次都是必须的。如果是两次握手,在第二次结束后,服务器并不能保证客户端已经收到了第二次的请求,如此一来的话,服务器会一直保存着这个通信过程,因为TCP通信都是要占用端口的,造成了一定的资源浪费。所以,就一定要让客户端来发送ACK的确认请求。

注:关闭的时候为什么会是四次挥手?

解:四次挥手不能像三次握手一样,三次握手可以将ACK+SYN 一起发送,ACK用于确认信息,SYN却是用来建立联机的;四次挥手中ACK是不能和FIN一起发送,ACK只是告诉客户端确认我收到了,等我将数据发送完毕之后会向其发送FIN的标志,所以四次挥手是不能够改变的。

26.Http协议和Https协议的区别是什么?

1.https协议需要花费一定的费用来获取到SSL的证书用来加密

2.http是超文本协议,信息是透明的。https是进行加密过后的协议,需要进行解密才能读取信息

3.二者痛的端口号不同,http使用的是80号端口,https使用的是443号端口

4.http链接比较简单,https进行连接时需要进行加密传输,身份认证

27.常见的Http状态码有哪些?

200 – 请求成功

301 – 资源(网页等)被永久转移到其它URL

404 – 请求的资源(网页等)不存在

500 – 内部服务器错误

28.ajax的请求方式有几种?

ajax请求方式有几种

29.ajax请求参数包括几个?

url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址。

type: 要求为String类型的参数,请求方式(post或get)默认为get。注意其他http请求方法,例如put和delete也可以使用,但仅部分浏览器支持。

timeout: 要求为Number类型的参数,设置请求超时时间(毫秒)。此设置将覆盖$.ajaxSetup()方法的全局设置。

async:要求为Boolean类型的参数,默认设置为true,所有请求均为异步请求。 如果需要发送同步请求,请将此选项设置为false。注意,同步请求将锁住浏览器,用户其他操作必须等待请求完成才可以执行。

cache:要求为Boolean类型的参数,默认为true(当dataType为script时,默认为false)。 设置为false将不会从浏览器缓存中加载请求信息。

data: 要求为Object或String类型的参数,发送到服务器的数据。如果已经不是字符串,将自动转换为字符串格 式。get请求中将附加在url后。防止这种自动转换,可以查看processData选项。对象必须为key/value格式,例如{foo1:“bar1”,foo2:“bar2”}转换为&foo1=bar1&foo2=bar2。如果是数组,jQuery将自动为不同值对应同一个名称。例如{foo:[“bar1”,“bar2”]}转换为&foo=bar1&foo=bar2。

dataType: 要求为String类型的参数,预期服务器返回的数据类型。如果不指定,JQuery将自动根据http包mime信息返回responseXML或responseText,并作为回调函数参数传递。可用的类型如下:

     xml:返回XML文档,可用JQuery处理。

     html:返回纯文本HTML信息;包含的script标签会在插入DOM时执行。

     script:返回纯文本JavaScript代码。不会自动缓存结果。除非设置了cache参数。注意在远程请求 时(不在同一个域下),所有post请求都将转为get请求。

     json:返回JSON数据。

     jsonp:JSONP格式。使用SONP形式调用函数时,例如myurl?callback=?,JQuery将自动替换后一个 “?”为正确的函数名,以执行回调函数。

     text:返回纯文本字符串。

beforeSend:要求为Function类型的参数,发送请求前可以修改XMLHttpRequest对象的函数,例如添加自定义HTTP头。在beforeSend中如果返回false可以取消本次ajax请求。XMLHttpRequest对象是惟一的参数。

       function(XMLHttpRequest){

          this;   //调用本次ajax请求时传递的options参数

       }

complete:要求为Function类型的参数,请求完成后调用的回调函数(请求成功或失败时均调用)。

     参数:XMLHttpRequest对象和一个描述成功请求类型的字符串。

     function(XMLHttpRequest, textStatus){

        this;    //调用本次ajax请求时传递的options参数

     }

success:要求为Function类型的参数,请求成功后调用的回调函数,有两个参数。

    (1)由服务器返回,并根据dataType参数进行处理后的数据。

    (2)描述状态的字符串。

    function(data, textStatus){

       //data可能是xmlDoc、jsonObj、html、text等等

       this;  //调用本次ajax请求时传递的options参数

error:要求为Function类型的参数,请求失败时被调用的函数。该函数有3个参数,即XMLHttpRequest对象、错误信息、捕获的错误对象(可选)。

  ajax事件函数如下:

  function(XMLHttpRequest, textStatus, errorThrown){

     //通常情况下textStatus和errorThrown只有其中一个包含信息

     this;   //调用本次ajax请求时传递的options参数

  }

contentType:要求为String类型的参数,当发送信息至服务器时,内容编码类型默认 为"application/x-www-form-urlencoded"。该默认值适合大多数应用场合。

dataFilter:要求为Function类型的参数,给Ajax返回的原始数据进行预处理的函数。提供data和type两个参数。data是Ajax返回的原始数据,type是调用jQuery.ajax时提供的

       dataType参数。函数返回的值将由jQuery进一步处理。

       function(data, type){

            //返回处理后的数据

            return data;

       }

global:要求为Boolean类型的参数,默认为true。表示是否触发全局ajax事件。设置为false将不会触发全局ajax事件,ajaxStart或ajaxStop可用于控制各种ajax事件。

ifModified:要求为Boolean类型的参数,默认为false。仅在服务器数据改变时获取新数据。服务器数据改变判断的依据是Last-Modified头信息。默认值是false,即忽略头信息。

jsonp:要求为String类型的参数,在一个jsonp请求中重写回调函数的名字。该值用来替代在"callback=?"这种GET或POST请求中URL参数里的"callback"部分,例如

  {jsonp:'onJsonPLoad'}会导致将"onJsonPLoad=?"传给服务器。

username:要求为String类型的参数,用于响应HTTP访问认证请求的用户名。

password:要求为String类型的参数,用于响应HTTP访问认证请求的密码。

processData:要求为Boolean类型的参数,默认为true。默认情况下,发送的数据将被转换为对象(从技术角度来讲并非字符串)以配合默认内容类型"application/x-www-form-urlencoded"。如果要发送DOM树信息或者其他不希望转换的信息,请设置为false。

scriptCharset:要求为String类型的参数,只有当请求时dataType为"jsonp"或者"script",并且type是GET时才会用于强制修改字符集(charset)。通常在本地和远程的内容编码不同时使用。

顺便说一下$.each()函数:

$.each()函数不同于JQuery对象的each()方法,它是一个全局函数,不操作JQuery对象,而是以一个数组或者对象作为第1个参数,以一个回调函数作为第2个参数。回调函数拥有两个参数:第1个为对象的成员或数组的索引,第2个为对应变量或内容。

线程

1.线程和进程的区别。

线程:是进程的一个实体,是 cpu 调度和分派的基本单位,是比进程更小的 可以独立运行的基本单位。

进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作 系统进行资源分配和调度的一个独立单位。

特点:线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行 时各自内存单元相互独立,线程之间 内存共享,这使多线程编程可以拥有更好 的性能和用户体验。

2.线程的生命周期

生命周期的五种状态

  1. 新建(new Thread) 当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。 例如: 1 Thread  t1=new Thread();
  2. 就绪(runnable) 线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源 例如: 1 t1.start();
  3. 运行(running) 线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
  4. 堵塞(blocked) 由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。 正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。 正在等待:调用wait()方法。(调用motify()方法回到就绪状态) 被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
  5. 死亡(dead) 当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。 自然终止:正常运行run()方法后终止 异常终止:调用stop()方法让一个线程终止运行

3.线程的实现方式有几种

有三种:

(1)继承Thread类,重写run函数

(2)实现Runnable接口,重写run函数

(3)实现Callable接口,重写call函数

4.线程池的实现原理是什么?

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。他的主要特点为:线程复用;控制最大并发数;管理线程。

线程复用:

每一个 Thread 的类都有一个 start 方法。 当调用 start 启动线程时

Java 虚拟机会调用该类的 run 方法。 那么该类的

run() 方法中就是调用了

Runnable 对象的 run() 方法。 我们可以继承重写

Thread 类,在其 start

方法中添加不断循环调用传递过来的 Runnable 对象。 这就是线程池的实现

原理。循环方法中不断获取 Runnable 是用 Queue 实现的,在获取下一个

Runnable 之前可以是阻塞的。

线程池的组成:

一般的线程池主要分为以下 4 个组成部分:

  1. 线程池管理器:用于创建并管理线程池
  2. 工作线程:线程池中的线程
  3. 任务接口:每个任务必须实现的接口,用于工作线程调度其运行
  4. 任务队列:用于存放待处理的任务,提供一种缓冲机制

拒绝策略:

线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已

经排满了,再也塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这

个问题。

JDK 内置的拒绝策略如下:

  1. AbortPolicy : 直接抛出异常,阻止系统正常运行。26
  2. CallerRunsPolicy : 只要线程池未关闭,该策略直接在调用者线程中,

运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的

性能极有可能会急剧下降。

  1. DiscardOldestPolicy : 丢弃最老的一个请求,也就是即将被执行的一

个任务,并尝试再次提交当前任务。

  1. DiscardPolicy : 该策略默默地丢弃无法处理的任务,不予任何处理。

如果允许任务丢失,这是最好的一种方案

Java 线程池工作过程:

  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,

就算队列里面有任务,线程池也不会马上执行它们。

  1. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:

a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这

个任务;

b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务

放入队列;

c) 如果这时候队列满了,而且正在运行的线程数量小于

maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

d) 如果队列满了,而且正在运行的线程数量大于或等于

maximumPoolSize,那么线程池会抛出异常 RejectExecutionException。

  1. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  2. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判

断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以

线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

5.线程池的实现方式有几种?

ExecutorService是线程池接口。它定义了4中线程池:

1.newCachedThreadPool:

底层:返回ThreadPoolExecutor实例,corePoolSize为0;maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为60L;unit为TimeUnit.SECONDS;workQueue为SynchronousQueue(同步队列)

通俗:当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。

适用:执行很多短期异步的小程序或者负载较轻的服务器

2.newFixedThreadPool:

底层:返回ThreadPoolExecutor实例,接收参数为所设定线程数量nThread,corePoolSize为nThread,maximumPoolSize为nThread;keepAliveTime为0L(不限时);unit为:TimeUnit.MILLISECONDS;WorkQueue为:new LinkedBlockingQueue() 无界阻塞队列

通俗:创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不在添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列),但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

适用:执行长期的任务,性能好很多

3.newSingleThreadExecutor:

底层:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例,corePoolSize为1;maximumPoolSize为1;keepAliveTime为0L;unit为:TimeUnit.MILLISECONDS;workQueue为:new LinkedBlockingQueue() 无界阻塞队列

通俗:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)

适用:一个任务一个任务执行的场景

4.NewScheduledThreadPool:

底层:创建ScheduledThreadPoolExecutor实例,corePoolSize为传递来的参数,maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为0;unit为:TimeUnit.NANOSECONDS;workQueue为:new DelayedWorkQueue() 一个按超时时间升序排序的队列

通俗:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构

适用:周期性执行任务的场景

6.sleep和wait的区别是什么?

最大的不同是在等待时 wait 会释放锁,而 sleep 一直持有锁。wait 通常 被用于线程间交互,sleep 通常被用于暂停执行。

7.同步锁和死锁有什么区别?

同步锁

当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。 Java 中可以使用 synchronized 关键字来取得一个对象的同步锁。

死锁

何为死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。

8.乐观锁和悲观锁的区别是什么?

乐观锁认为一个用户读数据的时候,别人不会去写自己所读的数据;悲观锁就刚好相反,觉得自己读数据库的时候,别人可能刚好在写自己刚读的数据,其实就是持一种比较保守的态度;时间 戳就是不加锁,通过时间戳来控制并 发出现的问题

悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据, 或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有等自己的整个事务提交了,才释放自己加上的锁,才允 许其他用户访问那部分数据。

9.线程池的核心参数有哪些?

默认参数:

corePoolSize = 1

queueCapacity = Integer.MAX_VALUE

maxPoolSize = Integer.MAX_VALUE

keepAliveTime = 60 秒

allowCoreThreadTimeout = false

rejectedExecutionHandler = AbortPolicy()

具体讲解:

1.corePoolSize(核心线程数)

(1)核心线程会一直存在,即使没有任务执行;

(2)当线程数小于核心线程数的时候,即使有空闲线程,也会一直创建线程直到达到核心线程数;

(3)设置 allowCoreThreadTimeout=true(默认 false)时,核心线程会超时关闭。

2.queueCapacity(任务队列容量)

也叫阻塞队列,当核心线程都在运行,此时再有任务进来,会进入任务队列,排队等待线程执行。

3.maxPoolSize(最大线程数)

(1)线程池里允许存在的最大线程数量;

(2)当任务队列已满,且线程数量大于等于核心线程数时,会创建新的线程执 行任务;

(3)线程池里允许存在的最大线程数量。当任务队列已满,且线程数量大于等于核心线程数时,会创建新的线程执行任务。

4.keepAliveTime(线程空闲时间)

(1)当线程空闲时间达到 keepAliveTime 时,线程会退出(关闭),直到线程

数等于核心线程数;

(2)如果设置了 allowCoreThreadTimeout=true,则线程会退出直到线程数等于零<allowCoreThreadTimeout(允许核心线程超时)>

当线程数量达到最大线程数,且任务队列已满时,会拒绝任务;

调用线程池 shutdown()方法后,会等待执行完线程池的任务之后,再shutdown()。如果在调用了 shutdown()方法和线程池真正 shutdown()之间提交任务,会拒绝新任务。

10.项目中哪些地方使用到了多线程?

项目中哪些地方使用到了多线程?

11.线程池的拒绝策略有哪些?

JDK内置4种线程池拒绝策略:

CallerRunsPolicy(调用者运行策略)

功能:当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程处理。

使用场景:一般在不允许失败的、对性能要求不高、并发量较小的场景下使用,因为线程池一般情况下不会关闭,也就是提交的任务一定会被运行,但是由于是调用者线程自己执行的,当多次提交任务时,就会阻塞后续任务执行,性能和效率自然就慢了。

AbortPolicy(中止策略)

功能:当触发拒绝策略时,直接抛出拒绝执行的异常,中止策略的意思也就是打断当前执行流程

使用场景:这个就没有特殊的场景了,但是一点要正确处理抛出的异常。

ThreadPoolExecutor中默认的策略就是AbortPolicy,ExecutorService接口的系列ThreadPoolExecutor因为都没有显示的设置拒绝策略,所以默认的都是这个。

但是请注意,ExecutorService中的线程池实例队列都是无界的,也就是说把内存撑爆了都不会触发拒绝策略。当自己自定义线程池实例时,使用这个策略一定要处理好触发策略时抛的异常,因为他会打断当前的执行流程。

DiscardPolicy(丢弃策略)

功能:如果线程池未关闭,就弹出队列头部的元素,然后尝试执行

使用场景:这个策略还是会丢弃任务,丢弃时也是毫无声息,但是特点是丢弃的是老的未执行的任务,而且是待执行优先级较高的任务。

基于这个特性,我能想到的场景就是,发布消息,和修改消息,当消息发布出去后,还未执行,此时更新的消息又来了,这个时候未执行的消息的版本比现在提交的消息版本要低就可以被丢弃了。因为队列中还有可能存在消息版本更低的消息会排队执行,所以在真正处理消息的时候一定要做好消息的版本比较。

设计模式

1.JAVA的设计模式了解过么?

常见的设计模式

2.单例模式里边的懒汉式和饿汉式区别是什么,可以手写么?

单例模式:某个类的实例在 多线程环境下只会被创建一次出来。

单例模式有饿汉式单例模式、懒汉式单例模式和双检锁单例模式三种。

饿汉式:线程安全,一开始就初始化。饿汉模式的意思是,我先把对象(面包)创建好,等我要用(吃)的直接直接来拿就行了。

public class Singleton {
 
 
//先把对象创建好
private static final Singleton singleton = new Singleton();
 
 private Singleton() {
    }
 
//其他人来拿的时候直接返回已创建好的对象
 
public static Singleton getInstance() {
 return singleton;
    }
}

懒汉模式

非线程安全,延迟初始化。因为饿汉模式可能会造成资源浪费的问题,所以就有了懒汉模式,

懒汉模式的意思是,我先不创建类的对象实例,等你需要的时候我再创建。

/**
 * 单例模式案例
 */
public class Singleton {
 
 private static Singleton singleton = null;
 
 private Singleton() {
    }
  //获取对象的时候再进行实例化
 public static Singleton getInstance() {
     synchronized (Singleton.class) {
 
 if (singleton == null) {
 singleton = new Singleton();
            }
 
   }
 return singleton;
    }
}

双检锁:线程安全,延迟初始化。

public static Singleton getInstance() {
if (singleton == null) {//先验证对象是否创建

synchronized (Singleton.class) {//只有当对象未创建的时候才上锁

if (singleton == null) {
singleton = new Singleton();
}

}
}
return singleton;
}

3.如何保证单例模式的安全性?

使用双检锁

4.spring框架中涉及了哪些设计

(1)工厂模式:BeanFactory 就是简单工厂模式的体现,用来创建对象的 实例;

(2)单例模式:Bean 默认为单例模式。

(3)代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节 码生成技术;

(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。

(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态 发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 Spring 中listener 的实现-ApplicationListener。

算法

1.常见的算法有哪些?

冒泡(要求手写),快排(要求手写),递归,二分查找

2.给你一个数组或者一个集合,如何进行快速排序?

了解Arrays

GC垃圾回收

1.GC 的垃圾回收有了解么?

简单了解GC垃圾回收

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值