【Java基础篇】Java面试题总结

系列文章目录

文章目录

前言

我想做的事情是,把Java面试题都收集总结一下,大家一起受用,因为里面可能也会有我自己的理解,如果你感觉有帮助到你,就给我点个赞哈 ~ ,如果里面发现有什么错误,也尽可以提出来补充补充,大家一起把这份面试题不断完善总结,一起受用呀~ ,不定时更新,每天几道面试题~~


一、Java基础篇

1.jdk jre jvm三者之间的关系

  1. jdk(java development kit):java开发工具包,提供了java开发环境和运行环境,同时还包含了编译java源码的编译器Javac里面包括jre
  2. jre(java runtime environment):java运行环境,里面包括jvm和java所需的核心库类
  3. jvm(java virtual machine):java虚拟机,负责java程序在该系统中运行

java开发者工具包 java运行环境 java虚拟机之间包含关系 :jdk>>jre>>jvm
jdk里面有jre,而jre里面有jvm,总的来说就是jdk包括jre和jvm

2.==和equals的区别是什么?

首先解释一下什么是引用变量?

1、要明白什么是变量,变量的实质是一小块内存单元,这一小块内存里存储着变量的值。

比如 int a=1;中a就是变量的名字,1就是变量的值。这里是变量的理解

而当变量指向一个对象的时候,这个变量就被称为引用变量

比如 A a=new A();a就是引用变量,它指向了一个A对象,也可以说它引用了一个A对象。我们通过操作这个a来操作A对象,此时,变量a的值为它所引用对象的地址。

科普一下基本类型变量和引用类型变量的区别:

对于基本类型变量:

在内存中存储的是一个基本类型值。
可以在栈中直接分配内存
例如 int a=1; 变量a的值就是int值1;

对于引用类型变量:

对应内存所存储的值是一个引用,是对象的存储地址。
对象的引用在栈中,对象实际存放在堆中。
例如 Circle circle = new Circle(9)【参数表示半径】; 变量circle 的值保存的是一个引用,它指明这个Circle对象的内容存储在内存的什么位置。

2、==操作符是专门用来比较两个变量的值是否相等的,也就是比较变量所对应的内存中所存储的数值是否相同,而变量又分为基本类型和引用类型两种,当两个基本类型的变量进行比较时,就是比较他们的值是否相等,当两个引用变量进行比较的时候就是比较引用对象的地址是否相等,例如:

String x=”string”;

String y=”string”;

String z=new String(“string”);

System.out.println(x==y);//true

System.out.println(x==z);//false

因为x和y指向的是同一个引用,故二者的地址是一样的,所以==是true,而new String()方法则重新开辟了内存空间,所以内存地址不一样,==结果为false。

如果一个变量指向的数据是对象类型,那么,这时候涉及了两块内存,对象本身占一块内存(堆内存),变量也占用一块内存,例如Object obj=new Object();变量obj是一个栈内存,new Object()是另一个堆内存,此时,变量obj所对应的内存中的数值就是对象占用的那块内存的首地址。

对于指向对象的变量,如果要比较两个变量是否指向同一个对象,就要看这两个变量所对应的内存中的数值是否相等,也就是比较对象占用的那块内存的首地址是否相等。这时候就要用==操作符进行比较。

3、equals()方法是用于比较两个变量是否指向同一个对象,不能作用于基本数据类型的变量,分为两种情况,第一种如果没有对Object中的equals()方法进行重写,这时候比较的是引用类型的变量所指的对象的地址,第二种是对Object中的equals()方法进行重写,这时候比较的是对象的内容是否相等,即堆内存中的内容是否相同。但是大多是类都重写的equals方法,所以我们平常时候的时候就是用的第二种情况,比较的是两个对象的内容是否相等。

String a=new String(“foo”);

String b=new String(“foo”);

两个new语句创建了两个对象,然后a,b这两个变量分别指向了各自的对象,这是两个不同的对象,他们的首地址是不同的,即a和b变量中存储的地址是不相同的,所以表达式a== b是false,而这两个对象中的内容是相同的,所以表达式a.equals(b)将返回true。在开发中字符串的比较基本上都是使用equals方法。如果一个类没有定义自己的equals方法,那它将继承Object类的equals方法,而Object类的equals方法内部就是用==操作符来判断的,也就是比较两个变量指向的对象是否是同一个对象,默认是引用比较,而很多类重写了equals方法,比如String,Integer等就把它变成了值比较,所以一般情况下equals比较的是值是否相等。所以一般如果你编写的类希望能够比较该类创建的两个实例对象是否相同,那么就必须覆盖equals方法,由自己写代码来决定在什么情况下可以认为两个对象的内容是相同的。

3.两个对象的hashCode()相同,则equals()也一定为true,对吗?

不对,hashCode相等,equals也不一定相等,equals相同,说明是同一个对象,那么hashCode一定相同,

为什么呢?因为hashCode和equals方法有一些规定:

两个对象用equals()比较返回true,那么两个对象的hashCode()方法必须返回相同的结果。
两个对象用equals比较返回false,不要求hashCode()方法一定返回不同的值,但是最好返回不同的值,以提高哈希表的性能。
重写equals()方法,必须重写hashCode()方法,以保证equals方法相等时两个对象的hashCode返回相同的值。

4.final在java中有什么用?

final 可以修饰类、方法、变量,final修饰的类不能被继承,final修饰的方法不能被重写,final修饰的变量叫常量,必须初始化而且不能被修改。

5.Math.round(11.5)等于多少?Math.round(-11.5)等于多少?

Math类中提供了三个与取整有关的方法:ceil、floor、round,这些方法的作用与它们的英文名称的含义相对应,例如,ceil的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3)的结果为12,Math.ceil(-11.3)的结果是-11;floor的英文意义是地板,该方法就表示向下取整,Math.ceil(11.6)的结果为11,Math.ceil(-11.6)的结果是-12;最难掌握的是round方法,它表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。

6.String是最基本的数据类型吗?

不是,基本数据类型有8中:整型(4种),字符型(1种),浮点型(2种),布尔型(1种),byte、short、int、long、char、float、double、boolean。

String类是引用数据类型

7.Java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有String、StringBuffer、StringBuilder。

String和StringBuffer、StringBuilder的区别在于String声明的是不可变的对象,每次操作都会生成新的String对象,然后将指针指向新的String对象,而StringBuffer、StringBuilder可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用String。StringBuffer、StringBuilder能自动扩容,而String是不可变的。

String覆盖了equals方法和hashCode方法,而StringBuffer没有覆盖equals方法和hashCode方法,所以将StringBuffer对象存储进java集合类中时会出现问题。

StringBuffer和StringBuilder最大的区别在于StringBuffer是线程安全的,因为StringBuffer有同步锁synchronized修饰,而StringBuilder没有,故是非线程安全的,但是StringBuilder的性能却高于StringBuffer,所以在单线程环境下推荐使用StringBuilder,多线程环境下推荐使用StringBuffer

8. String str="i"与 String str=new String(“i”)一样吗?

不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

String s1 = "china";
String s2 = "china";
String s3 = "china";

String ss1 = new String("china");
String ss2 = new String("china");
String ss3 = new String("china");
s1==s2==s3; //true
ss1!=ss2!=ss3!=s1!=s2!=ss3; //true
ss1.eqauls(ss2)//true
ss1.eqauls(ss3)//true
ss1.eqauls(s1)//true
ss1.eqauls(s2)//true
ss1.eqauls(s3)//true

在这里插入图片描述
对于字符串,其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
可以看出s1、s2、s3在编译期就被创建,并存入到了常量池中。编译器在执行String s1 = “china”;时会先在常量池中查找是否存在字符串常量“china”,如果不存在就在常量池中new一个china字符串,存在就不new,然后让栈中的变量指向这个china字符串。因此常量池中只有一个china字符串对象,然后在执行String s2 = “china”;String s3 = “china”;时,会在常量池中找到china字符串,并让s2、s3指向它。
对于String ss1 = new String(“china”);在编译时并不会创建,在运行时,通过new产生一个字符串(假设为“china”)时,会先去常量池中查找是否已经存在字符串“china”,如果不存在则在常量池中创建一个“china”字符串对象,然后在堆中再创建一个常量池中的“china”对象的拷贝对象;如果常量池中存在,就直接在堆中创建一个常量池中此“china”对象的拷贝对象。

9.如何将字符串反转?

1,使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
2,利用 String 的 toCharArray 方法先将字符串转化为 char 类型数组,然后将各个字符进行重新拼接.

10. String 类的常用方法都有那些?

IndexOf():返回指定字符的索引。
charAt():返回指定索引处的字符
replace():字符串替换。
trim():去除字符串两端空白
split():分割字符串,返回一个分割后的字符串数组。
length():返回字符串长度。
toLowerCase():将字符串转换成小写字母
toUpperCase():将字符串转换成大写字母
substring():截取字符串
equals():字符串比较

11. 抽象类必须要有抽象方法吗?

不需要,抽象类不一定非要有抽象方法。

12. 普通类和抽象类有哪些区别?

普通类不能包含抽象方法,抽象类可以包含抽象方法
抽象类不能直接实例化,普通类可以直接实例化。

13. 抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为final该类就不能被继承,这样彼此就会产生矛盾,所以final不能修饰抽象类。

14. 接口和抽象类有什么区别?

1,一个类可以实现多个接口,但是只能继承一个抽象类。
2,抽象类可以有默认方法实现,接口不能有默认方法实现。
3,抽象类的子类使用extends来继承,接口必须使用implements来实现接口。
4,抽象类只能被继承不能直接实例化。

15. Java 中 IO 流分为几种?

输入流:InputStream(字节输入流)、Reader(字符输入流)
输出流:OutputStream(字节输出流)、Writer(字符输出流)
字节(Byte)是一种计量单位,表示数据量多少,它是计算机信息技术用于计量存储容量的一种计量单位。
字符是指计算机中使用的文字和符号,比如1、2、3、A、B、C、~!·#¥%……—*()——+、等等。

16.Files 的常用方法都有哪些?

Files.exists():检测文件路径是否存在。
Files.createFile():创建文件。
Files.createDirectory():创建文件夹。
Files.delete()删除一个文件和目录。
Files.copy()复制文件。
Files.move()移动文件。
Files.size()查看文件个数。
Files.read():读取文件。
File.write():写入文件。

17.什么是面向对象和面向过程,谈谈你的理解?

这里我还没想到好的答案,先空着

二、Java集合容器

1. Java集合容器都有哪些?

Java集合容器分为Collection接口和Map接口两大类,其中Collection下面又有List和Set,List又分为ArrayList和LinkedList
和Vector。Set又分为HashSet和TreeSet。Map又分为HashMap、TreeMap
下面是常用的几个集合容器:

在这里插入图片描述
Collection
|–List:元素是有序的元素可以重复,因为该体系有索引。

  • ArrayList:底层的数据结构使用的是数组结构,特点:查询速度很快,但增删稍慢,线程不同步。
  • LinkedList:底层使用的是链表数据结构。特点:增删速度快,查询稍慢。
  • Vector:底层是数组数据结构,线程同步,被ArrayList替代。(已弃用

|–Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。

  • HashSet:底层数据结构是哈希表。线程是非同步。
    HashSet是如何保证元素唯一性的呢?
    是通过元素的两个方法,hashCode和equals来完成,如果元素的HashCode值相同,才会判断equals是否为true
    如果元素的hashcode值不同,不会调用equals, 注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。

  • TreeSet:可以对Set集合中的元素进行排序。
    底层数据结构是二叉树
    保证元素唯一性的依据,compareTo方法return 0;
    TreeSet排序的第一种方式:让元素自身具备比较性,元素需要实现Comparable接口,覆盖comparaTo方法, 这种方式也成为元素的自然顺序,或者叫做默认顺序。
    第二种方式:当元素自身不具备比较性,或者具备的比较性不是所需要的,这时需要让容器自身具备比较性,定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。

当两种排序都存在时,以比较器为主,定义一个类,实现Comparator接口,覆盖compare方法。

Map

  • HashMap:底层是哈希表数据结构,运行使用null和null键,该集合是不同步的,jdk1.2效率高。
  • TreeMap:底层是二叉树数据结构,线程不同步,可以用于给map集合中的键进行排序。

2. Collection和Collections有什么区别?

Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如List和Set。
Collections是一个包装类,它包含有各种有关集合操作的静态方法(对集合的搜索、排序、线程安全化等),此类不能实例化,就像一个工具类,服务于Java的Collection框架。

3.HashMap 和 Hashtable 有什么区别?

存储:HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。而Hashtable既不支持Null key也不支持Null value。
线程安全:Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步

HashMap不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题。具体的原因在下一篇文章中会详细进行分析。使用HashMap时就必须要自己增加同步处理。

虽然HashMap不是线程安全的,但是它的效率会比Hashtable要好很多。这样设计是合理的。在我们的日常使用当中,大部分时间是单线程操作的。HashMap把这部分操作解放出来了。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。

4. 如何决定使用HashMap还是TreeMap?

对于在Map中插入、删除、定位一个元素这类操作,HashMap是最好的选择,因为HashMap的插入很快,但是如果你要对一个key集合进行有序的遍历,那TreeMap是更好地选择。

5. 说一下HashMap的实现原理?

目前理解比较简单,先简单介绍下吧,等找到好的资料再深入理解
HashMap基于Hash算法实现的,我们通过put(key,value)存储,通过get(key)来获取。当传入key时,HashMap就会根据key.hashCode()计算出hash值,然后根据hash值在表中搜索对应的索引,这个索引就是元素在数组中的下标,根据hash值将value保存在bucket里。如果该数组位置上已经有其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放入链头,最先加入的放入链尾。当计算出的hash值相同时,我们称为hash冲突,HashMap的做法是用链表和红黑树存储相同的hash值的value。

6. 说一下 HashSet 的实现原理?

HashSet是基于HashMap实现的,HashSet底层使用HashMap来保存所有的元素,因此HashSet的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成,HashSet不允许有重复的值。
在HashSet中,基本的操作都是有HashMap底层实现的,因为HashSet底层是用HashMap存储数据的。当向HashSet中添加元素的时候,首先计算元素的hashcode值,然后用这个(元素的hashcode)%(HashMap集合的大小)+1计算出这个元素的存储位置,如果这个位置位空,就将元素添加进去;如果不为空,则用equals方法比较元素是否相等,相等就不添加,否则找一个空位添加。

7. ArrayList 和 LinkedList 的区别是什么?

1.数据结构实现:ArrayList是动态数组的数据结构,而LinkedList是双向链表的数据结构实现的。
2.随机访问效率:ArrayList比LinkedList在随机访问的时候效率要高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后一次查找。
3.增加和删除效率:在非首尾的增加和删除操作,LinkedList要比ArrayList效率高,因为ArrayList增删操作要影响数组内的其他数据的下标。
4.综合来说,在需要频繁读取集合中的元素时,更推荐使用ArrayList,而在插入和删除操作较多时,更推荐使用LinkedList。

8. 如何实现数组和List之间的转换?

1.数组转List:使用Arrays.asList(array)进行转换。
注意:

  • 该方法适用于对象型数据的数组(String、Integer…)
  • 该方法不建议使用于基本数据类型的数组(byte,short,int,long,float,double,boolean)
  • 该方法将数组与List列表链接起来:当更新其一个时,另一个自动更新
  • 不支持add()、remove()、clear()等方法
  • 转为list的时候要用泛型list接收,然后用增强for循环或者遍历器

2.List转数组:使用List自带的toArray()方法。

9. ArrayList和Vector的区别是什么?

线程安全:Vector只要是关键性的操作,方法前面都加了synchronized关键字,来保证线程的安全性,是线程安全的,而ArrayList没有使用,是非线程安全的。
性能:ArrayList在性能方面要优于Vectory(因为当执行synchronized修饰的方法前,系统会对该方法加一把锁,方法执行完成后释放锁,加锁和释放锁的这个过程,在系统中是有开销的,因此,在单线程的环境中,Vector效率要差很多。(多线程环境不允许用ArrayList,需要做处理))
扩容:ArrayList和Vector都会根据实际的需要动态的调整容量,只不过Vector扩容每次会增加1倍,而ArrayList只会增加50%。

10.数组Array和列表ArrayList有何区别?

数组Array可以存储基本数据类型和对象,列表ArrayList只能存储对象。
数组Array是固定大小的,而列表ArrayList大小是自动扩展的。
数组Array内置方法没有列表ArrayList多,比如addAll()、removeAll()、iteration等方法只有ArrayList有。

11. 迭代器Iterator是什么?

Iterator接口提供遍历任何Collection的接口,我们可以从一个Collection中使用迭代器方法来获取迭代器实例。迭代器取代了 Java集合框架中的Enumeration,迭代器允许调用者在迭代过程中移除元素。

12.迭代器Iterator怎么使用?有什么特点?

Iterator使用代码如下:
List list=new ArrayList<>();
Iterator it=list.iterator();
While(it.hasNext()){
String obj-=it.next();
System.out.println(obj);
}
Iterator的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出ConcurrentModificationException的异常。
解释:
hasNext:没有指针下移操作,只是判断是否存在下一个元素
next:指针下移,返回该指针所指向的元素

三、多线程

1.并发和并行有什么区别?

并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。

首先并发,是针对一个cpu来说的,实际上,对于单CPU的计算机来说,在CPU中,同一时间是只能干一件事儿的。为了看起来像是“同时干多件事”,Windows这种操作系统是把CPU的时间划分成长短基本相同的时间区间,即”时间片”,通过操作系统的管理,把这些时间片依次轮流地分配给各个应用使用。

这样,给用户的感觉是他在同时的进行听歌和打游戏,实际上,在操作系统中,CPU是在游戏进程和音乐播放器进程之间来回切换执行的。由于计算机的处理速度很快,只要时间片的间隔取得适当,那么一个用户作业从用完分配给它的一个时间片到获得下一个CPU时间片,中间有所”停顿”,但用户察觉不出来,实现了所谓的『同时进行』。

然后有多个CPU才会出现并行。在有多个CPU的情况下,才会出现真正意义上的『同时进行』。

并行(Parallel),当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。

2. 线程和进程的区别?

进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;
线程:进程之内独立执行的一个单元执行流。
进程——资源分配的最小单位,线程——程序执行的最小单位。

3.创建线程有哪几种方式?

创建线程有三种方式:
1, 继承Thread并重写run方法
2, 实现Runnable接口并重写run方法
3, 实现Callable接口并重写 call() 方法

4.说一下Runnable接口和Callable接口有什么区别?

Runnable没有返回值,callable可以拿到返回值,callable可以看作runnable的补充。

5.线程有哪些状态?

1.新建状态
当用new操作符创建一个线程时。此时程序还没有开始运行线程中的代码。

2.就绪状态
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序来调度的。

3.运行状态(running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。

4.阻塞状态(blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:

①线程通过调用sleep方法进入睡眠状态;

②线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;

③线程试图得到一个锁,而该锁正被其他线程持有;

④线程在等待某个触发条件;

所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

5.死亡状态(dead)
有两个原因会导致线程死亡:

①run方法正常退出而自然死亡;

②一个未捕获的异常终止了run方法而使线程猝死;

为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法,如果是可运行或被阻塞,这个方法返回true;如果线程仍旧是new状态且不是可运行的,或者线程死亡了,则返回false。

6.sleep()和wait()有什么区别?

1,sleep() 是 Thread 类的静态本地方法;wait() 是Object类的成员本地方法
2,sleep() 会休眠当前线程指定时间,释放 CPU 资源,不释放对象锁,休眠时间到自动苏醒继续执行;wait() 方法放弃持有的对象锁,进入等待队列,当该对象被调用 notify() / notifyAll() 方法后才有机会竞争获取对象锁,进入运行状态

7.notify()和notifyAll()有什么区别?

notifyAll()会唤醒所有的线程,notify()则唤醒一个线程。notifyAll()调用后,会将全部线程由等待池移动到锁池,然后参与锁的竞争,竞争成功后则继续执行,如果不成功则留在锁池等待被释放后再次参与竞争,而notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。

8.线程的run()方法和start()有什么区别?

start()方法用于启动线程,run()方法用于执行线程的运行时的代码,run()可以重复调用,而start()只能调用一次。

9.线程池都有哪些状态?

1.RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。线程池的初始化状态是RUNNING。线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。

2.SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。调用线程池的shutdown()方法时,线程池由RUNNING -> SHUTDOWN。

3.STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。调用线程池的shutdownNow()方法时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4.TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。因为terminated()在ThreadPoolExecutor类中是空的,所以用户想在线程池变为TIDYING时进行相应的处理;可以通过重载terminated()函数来实现。

当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。

当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5.TERMINATED:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

10.线程池优势和核心参数

1,线程池的优势:
避免频繁的创建线程和销毁线程,使线程得以复用,降低CPU的消耗,提升服务器性能。

2,线程池的核心参数:
corePoolSize:核心池大小,当线程池提交一个任务时,若线程池创建的线程小于核心池大小,就会创建一个新的线程来执行该任务,直到创建的线程数大于核心池时,就会把任务放到缓存队列中,除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止,默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:空闲线程存活时间单位
workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。
threadFactory线程工厂:创建一个新线程时使用的工厂,可以用来设定线程名等
handler拒绝策略:当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,
在JDK1.5中java线程池框架提供了4种策略:
AbortPolicy:直接抛出异常
CallerRunsPolicy:只用调用者所在线程来运行任务
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
DiscardPolicy:不处理,丢弃掉。

总结

我想做的事情是,把Java面试题都收集总结一下,大家一起受用,因为里面可能也会有我自己的理解,如果你感觉有帮助到你,就给我点一点赞哈~ ,如果里面发现有什么错误,也尽可以提出来补充补充,大家一起把这份面试题不断完善总结,一起受用呀~ ,不定时更新,每天几道面试题~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值