Java面试宝典

JAVA面试宝典2021

一、Java 基础

1.JDK  JRE 有什么区别?

JRE Java Runtime Environment)是Java 运行时环境……它是运行编译后的Java程序所必需的一切包,包括Java虚拟机(JVM)Java基础类库、Java 命令和其他基础设施。但是,它不能用于创建新程序。

JDKJava 开发工具包……功能齐全的SDKforJava。它拥有JRE所拥有的一切,还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsolejvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。它能够创建和编译程序,是提供给程序员使用的。

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

1、功能不同

"=="是判断两个变量或实例是不是指向同一个内存空间的值

"equals"是判断两个变量或实例所指向的内存空间的值是不是相同。

2、定义不同

"equals"JAVA中是一个方法。

"=="JAVA中只是一个运算符合

  1. Java中基本类型变量有byte、short、char、int、long、float、double、boolean,也称为原始数据类型。他们之间的比较应用双等号(==)比较他们的值,当使用==来判断两个变量是否相等时,如果这两个变量是基本数据类型,则只要两个变量值相等就放回true
  2. 复合数据类型(类)

当使用==比较他们两个的值,比较的是他们的变量指向的地址值,除非这两个变量是同一个NEW出来的对象,则他们的

比较为true,反之为false

 Equals()方法:

Java中所有的类都继承与基类Object,在Object中定义了equals方法,这个方法的初始行为是比较对象的内存地址。但是在一些类库中这个方法被重写了,如String、Integer、Date这些类中的equals方法有自身的实现,而不再是比较内存

地址值。

所有说对用复合型数据类型(引用类型),我们推荐使用重写了的equals方法进行比较,

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

两个对象equals相等,则它们的hashcode必须相等,反之则不一定。两个对象==相等,则其hashcode一定相等, 反之不一定成立。

hashCode(哈希码值)的存在主要应用于查找的快捷性,如hashTable、hashMap等,hashCode码是确定对象在散列存储结构中的地址值。

如果两个对象通过equals方法比较相等,那么他们的hashCode值也一定相等。反之hashCode码相等,但是连个对象的equals比较不一定相等,也就是说这两个对象之间不一定适用于equals方法,只能说明这两个对象都存在于散列存储结构中。

4.final  java 中有什么作用?(在Java中final可以修饰类、变量、方法)

(1)Final修饰类:

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

(2)Final修饰方法:当final修饰的方法后该方法不能被子类重写(可以重载多个final修饰的方法), 注意:重写的前提是子类可以从父类中继承此方法,如果父类中用final修饰的方法同时访问修饰符是private时,子类中则可以定义相同的方法名和参数

(3)Final修饰变量: final在修饰基本数据类型变量时,该变量一经赋值则不许改变,如果修饰的引用数据类型时,则代表着引用的地址值不能改变,但地址值所指向的内容可以改变。

①修饰成员变量时:修饰成员变量时必须显示初始化,初始化方式有两种,第一就是变量声名时直接初始化,二就是变量声明后不初始化,但必须要在这个变量所在的类的所有构造函数中对这个变量进行赋值。

②修饰局部变量时:当函数的参数通过final进行定义时,代表着该参数为只读,可以读取使用该参数,但不能修改该参数值。

5.java 中的 Math.round(-1.5) 等于多少?

Mathround方法是四舍五入,如果参数是负数,则往大的数如,Math.round(-1.5)=-1

6.String 属于基础的数据类型吗?(引用数据类型) 

String并不是基本数据类,而是一个类(class),是C++java等编程语言中的字符串,

Java中八大基本类型数据:

1字符类型: char  2基本整型:byte,short,int,long  3浮点型:float,double 4 布尔类型:boolean”

7.java中操作字符串都有哪些类?(String、StringBuffer、StringBuilder)它们之间有什么区别?

String : final修饰,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。 StringBuffer : 对字符串的操作的方法都加了synchronized,保证线程安全。

StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用 StringBuilder对象的appendreplacedelete等方法修改字符串。

  1. String是不可变字符 StringBuffer、StringBuilder是可变字符。三者的底层都是以char[]形式保存字符

String底层初始化时默认char[]数组长度为0,StringBuffer、StringBuilder初始化默认的char[]数组长度为16

(因为String不可变,每次对String进行操作都会生成新的字符

  1. StringBuffer中大部分的方法都是被synchronized关键字这就代表着StringBuffer是线程安全的。StringBuilder

中的方法则没有,代表着他是一个线程不安全的。所以在单线程的情况下选择StringBuilder要更快,然而在多线程当中

考虑使用StringBuffer更加安全些

  1. 在生命变量的时候如果根据情况这个变量声明之后基本上不做修改我自己考虑直接使用String,不同new String()的

方式进行声明那样就不会再堆内存中创建对象,此时String变量直接指向常量池,并且可以复用,效率更高

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

 String str = “i”代表着声明了一个变量str,此时在常量池中创建了一个内容为i的字符窜对象。

 String str = new String(“I”);此时代表着创建了两个对象,str引用地址指向堆内存,如果常量池中没有字符窜i,则会

 在常量池中创建第二个对象字符窜i。

9.如何将字符串反转?

1. 利用 StringBuffer  StringBuilder  reverse 成员方法:

  public static String reverse(String str) {

return new StringBuilder(str).reverse().toString();

}

2. 利用 String  toCharArray 方法先将字符串转化为 char 类型数组,然后将各个字符进行重新拼接:

//自定义方法

public static String reverse(String str){
    char[] chars = str.toCharArray();
    //创建StringBuilder对象进行拼接
    StringBuilder builder = new StringBuilder();
    for (int i = chars.length - 1; i >= 0; i--) {
        builder.append(chars[i]);
    }
    return builder.toString();
}

3. 利用 String  CharAt 方法取出字符串中的各个字符:

public static String reverse1(String str){
    //创建StringBuilder对象进行拼接
    StringBuilder builder = new StringBuilder();
    //获取字符窜长度
    int length = str.length();
    for (int i = length-1; i >=0; i--) {
        builder.append(str.charAt(i));
    }
    return builder.toString();
}

4.使用递归的方式进行反转

public static String reverse2(String str){
    //获取字符窜长度
    int length = str.length();
    if(length<=1){
        return str;
    }
    String left = str.substring(0, length/2);
    String right = str.substring(length/2,length);
    return reverse2(right)+reverse2(left);
}

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

indexOf(String str):查找指定的字符在当前字符窜第一次出现的索引值

charAt(int index) 返回指定索引处得字符

replace(char oldChar,char newChar):  它是通过用 newChar 替换此字符串中出现的所有 oldChar 

trim() 去除字符串两端的空白

split() 分割字符串 返回分割后的字符串数组 

getBytes() 返回字符串的byte类型数组 

length() 返回字符串的长度

toLowerCase() 字符串转小写 

toUpperCase() 字符串转大写 

substring() 截取字符串 

equals() 字符串比较

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

抽象类可以没有抽象方法,但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,它也不能再实例化,即不能直接构造一个该类的对象。 如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。

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

抽象类不能被实例化

抽象类可以有抽象方法,抽象方法只需申明,无需实现

含有抽象方法的类必须申明为抽象类

抽象类的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类

抽象方法不能被声明为静态

抽象方法不能用 private 修饰

抽象方法不能用 final 修饰

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

不能,抽象类是被用于继承的,final修饰代表不可修改、不可继承的。

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

1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。

2、抽象类要被子类继承,接口要被类实现。

3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现

4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。

6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果

7、抽象类里可以没有抽象方法

8、如果一个类里有抽象方法,那么这个类只能是抽象类

9、抽象方法要被实现,所以不能是静态的,也不能是私有的。

10、接口可继承接口,并可多继承接口,但类只能单根继承。

15.java  IO 流分为几种?

Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStreamOutputStreamReaderWriterJava中其他多种多样变化的流均是由它们派生出来的。

16.BIONIOAIO 有什么区别?

BIO是一个连接一个线程。 NIO是一个请求一个线程。 AIO是一个有效请求一个线程。

BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 

NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 

AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

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

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

二、容器

 18.java 容器都有哪些?

List,Map,Set Collection List LinkedList ArrayList Vector Stack Set Map Hashtable HashMap WeakHashMap

数据容器主要分为了两类: Collection: 存放独立元素的序列。 Map:存放key-value型的元素对。(这对于需要利用key查找value的程序十分的重要!) 从类体系图中可以看出,Collection定义了Collection类型数据的最基本、最共性的功能接口,而List对该接口进行了拓展。 LinkedList :其数据结构采用的是链表,此种结构的优势是删除和添加的效率很高,但随机访问元素时效率较ArrayList类低。 ArrayList:其数据结构采用的是线性表,此种结构的优势是访问和查询十分方便,但添加和删除的时候效率很低。 HashSet: Set类不允许其中存在重复的元素(集),无法添加一个重复的元素(Set中已经存在)。HashSet利用Hash函数进行了查询效率上的优化,其contain()方法经常被

使用,以用于判断相关元素是否已经被添加过。 HashMap: 提供了key-value的键值对数据存储机制,可以十分方便的通过键值查找相应的元素,而且通过Hash散列机制,查找十分的方便。

19.Collection  Collections 有什么区别?

Collection 是集合的接口,其继承类又List Set

Collections 是集合的工具类,定义了许多操作集合的静态方法。是帮助类

20.ListSetMap 之间的区别是什么?

List:有序集合

Set:不重复集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序

Map:键值对集合

(1)元素的重复性:

List集合中可以出现重复元素

Set集合中不可以出现重复元素,在向Set集合中存储多个重复的元素时会出现覆盖

Map集合采用key-value的形式存储,在Map中不能出现重复的Key键,但可以出现多个不同的Key对应相同的Value

(2)元素的有序性:

List集合及其所有的实现类都确保了元素的有序性

Set集合中的元素是无序的,但是某些Set集合通过特定的形式对其中的元素进行排序,如LinkedHashSet

Map和Set一样对元素进行了无序存储,如:TreeMap根据Key键实现了元素的升序排序

(3)元素是否为空值:

List集合中允许存在多个空值

Set最多允许一个空值出现

Map中只允许出现一个空键,但允许出现多个空值

21.HashMap  Hashtable 有什么区别?

1 HashMap不是线程安全的 hastmap是一个接口 是map接口的子接口,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值。HashMap允许null keynull value 2 HashTable是线程安全的一个Collection HashMapHashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key,由于非线程安全,效率上可能高于Hashtable HashMap允许将null作为一个entrykey或者value,而Hashtable不允许。 HashMapHashtablecontains方法去掉了,改成containsvaluecontainsKey。因为contains方法容易让人引起误解。 Hashtable继承自Dictionary类,而HashMapJava1.2引进的Map interface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访

Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。 HashtableHashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

22.如何决定使用 HashMap 还是 TreeMap

TreeMap<K,V> Key值是要求实现 java.lang.Comparable ,所以迭代的时候TreeMap默认是按照Key值升序排序的;

TreeMap的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。

HashMap<K,V> Key值实现散列 hashCode() ,分布是散列的、均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。适用于在Map中插入、删除和定位元素。

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

HashMap使用数组加链表实现。每个数组中储存着链表。当使用put方法储存key-value键值对时,会先调用keyhashCode方法,得到此key经特定哈希运算后的值,然后将此值通过其他运算(?)得到一个值,将这个值与(length-1)做或操作(&),相当于对数组长度做取余操作。最终得到一个值作为此key在数组中的索引值,然后将key-value键值对储存进去。通过这种方法将储存的不同key-value键值对散列到数组的不同位置。

在储存的时候,如果索引位置尚无元素,那么直接储存。如果有元素,那么就调用此keyequals方法与原有的元素的Key进行比较。如果返回true,说明在这个equals定义的规则上,这两个Key相同,那么将原有的key保留,用新的value代替原来的value。如果返回false,那么就说明这两个keyequals定义的规则下是不同元素,那么就与此链表的下一个结点进行比较,知道最后一个结点都没有相同元素,再下一个是null的时候,就用头插法将此key-value添加到链表上。

HashMap对重复元素的处理方法是:key不变,value覆盖。

当使用get方法获取key对应的value时,会和储存key-value时用同样的方法,得到key在数组中的索引值,如果此索引值上没有元素,就返回null。如果此索引值上有元素,那么就拿此keyequals方法与此位置元素上的key进行比较,如果返回true。就返回此位置元素对应的value。如果返回false,就一直按链表往下比较,如果都是返回false,那么就返回null

另外:HashMapJDK1.8之后引入红黑树结构。HashMap是线程不安全的,线程安全的是CurrentHashMap,不过此集合在多线程下效率低。

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

首先,我们需要知道它是Set的一个实现,所以保证了当中没有重复的元素。 一方面Set中最重要的一个操作就是查找。而且通常我们会选择HashSet使用的是散列函数,那么它当中的元素也就无序可寻。当中是允许元素为null的。

25.ArrayList  LinkedList 的区别是什么?

数据结构实现: ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 • 随机访问效

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

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

List转数组:toArray()方法

数组转ListArraysasList(a)方法

27.ArrayList  Vector 的区别是什么?

这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是与HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素。

ArrayListVector的区别主要包括两个方面:.

1)同步性:

的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。

2)数据增长:

要增加ArrayListVector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍) ArrayListVector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。

28.Array  ArrayList 有何区别?

ArrayJava中的数组,声明数组有三种方式 int[] a=new int[10]; int a[]=new int[10]; int a[]={1,2,3,4}; 可以看出:在定义一个数组的时候,必须指定这个数组的数据类型及数组的大小,也就是说数组中存放的元素个数固定并且类型一样

ArrayList是动态数组,也就是数组的复杂版本,它可以动态的添加和删除元素,被称为集合,集合的声明如下ArrayList list = new ArrayList(10); ArrayList list1 = new ArrayList(); 可以看出:在不使用泛型的情况下,这个list是可以添加进不同类型的元素的,而且arraylist是可以不用指定长度的。在使用泛型时,我们就只能添加一种类型的数据了

29. Queue  poll() remove()有什么区别?

相同点:都是返回第一个元素,并在队列中删除返回的对象。

poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。

30.哪些集合类是线程安全的?

Vector:就比Arraylist多了个同步化机制(线程安全)。

Hashtable:就比Hashmap多了个线程安全。

ConcurrentHashMap:是一种高效但是线程安全的集合。

Stack:栈,也是线程安全的,继承于Vector

31.迭代器 Iterator 是什么?

首先说一下迭代器模式,它是 Java 中常用的设计模式之一。用于顺序访问集合对象的元素,无需知道集合对象的底层实现。Iterator 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦。

缺点是增加新的集合类需要对应增加新的迭代器类,迭代器类与集合类成对增加。

32.Iterator 怎么使用?有什么特点?

1Iterator()要求容器返回一个IteratorIterator将准备好返回序列的第一个元素。

2)使用next()获得序 列中的下一个元素

3)使用hasNext()检查序列中是否还有元素。

4)使用remove()将迭代器新近返回的元素删除。

有什么特点:

1 Iterator遍历集合元素的过程中不允许线程对集合元素进行修改,否则会抛出

ConcurrentModifificationEception的异常。

2Iterator遍历集合元素的过程中可以通过remove方法来移除集合中

的元素,删除的是上一次Iterator.next()方法返回的对象。

3Iterator必须依附于一个集合类对象而存在,Iterator本身不具有装载数据对象的功能。

4next()方法,该方法通过游标指向的形式返回Iterator下一个元

素。

33.Iterator  ListIterator 有什么区别?

1)所属关系,ListIterator是一个Iterator的子类型。 (2)局限:只能应用于各种List类的访问。 (3)优势:

Iterator只能向前移动,而ListIterator可以双向移动。 (4ListIterator  add() 方法,可以向 List 中添加对象,而

Iterator 不能。

34.怎么确保一个集合不能被修改?

我们可以采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报

java.lang.UnsupportedOperationException错。

同理:Collections包也提供了对listset集合的方法。 Collections.unmodififiableList(List)

Collections.unmodififiableSet(Set)

三、多线程

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

解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。

解释二: 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。

解释三:在一台处理器上同时处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群 所以并发编程的目标是充分的利用处理器的每一个核,以

达到最高的处理性能。

36.线程和进程的区别?

进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫轻量级进程。

线程的划分小于进程,线程是隶属于某个进程的。进程是程序的一种动态形式,是CPU,内存等资源占用的基本单

位,而线程是不能占有这些资源的。

进程之间相互独立,通信比较困难,而线程之间共享一块内存区域,通信比较方便。

进程在执行过程中包含比较固定的入口,执行顺序,出口,而线程的这些过程会被应用程序所控制。

37.守护线程是什么?

1、守护线程,专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完

毕,那么jvm就会退出(即停止运行)——此时,连jvm都停止运行了,守护线程当然也就停止执行了。

2、再换一种说法,如果有用户自定义线程存在的话,jvm就不会退出——此时,守护线程也不能退出,也就是它还

要运行,干嘛呢,就是为了执行垃圾回收的任务啊。

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

1,继承Thread类,重写run方法; 

2,实现Runnable接口,重写run方法,但是比继承Thread类好用,实现接口还 可以继承类,避免了单继承带来的局限性; 3,使用Executor框架创建线程池。Executor框架是juc里提供的线程池 的实现。 调用线程的start():启动此线程;调用相应的run()方法

Thread的常用方法: 

1.start():启动线程并执行相应的run()方法 

2.run():子线程要执行的代码放入run()方法中

3.currentThread():静态的,调取当前的线程 

4.getName():获取此线程的名字 

5.setName():设置此线程的名字

6.yield():调用此方法的线程释放当前CPU的执行权(很可能自己再次抢到资源) 

7.join():A线程中调用B线程的join()

方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕, A线程再接着join()之后的代码执行

8.isAlive():判断当前线程是否还存活 

9.sleep(long l):显式的让当前线程睡眠l毫秒 (只能捕获异常,因为父类run方法没

有抛异常)

10.线程通信(方法在Object类中):wait() notify() notifyAll() *设置线程的优先级(非绝对,只是相对几

率大些)

11.getPriority():返回线程优先值 setPriority(int newPriority):改变线程的优先级

39.说一下 runnable  callable 有什么区别?

、相同点:

1. 两者都是接口;(废话)

2. 两者都可用来编写多线程程序;

3. 两者都需要调用Thread.start()启动线程

不同点:

1. 两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返

回结果;

  1. Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

40.线程有哪些状态?

线程状态有 5 种,新建,就绪,运行,阻塞,死亡 

新建状态:创建一个新的线程对象

就绪:新的线程对象调用start()方法,该状态下的线程位于可运行的线程池中,等待获取CPU调度时间

运行:线程池中的线程获取到了CPU的资源,开始执行程序

阻塞: 指运行状态下的线程因为某种原因放弃了CPU使用权,暂时停止运行,直到线程再次进入就绪状态才有机会获取CPU的

      资源进入到运行状态

(1)、等待阻塞:运行中的线程执行了wait()方法,JVM会把该线程放入等待队列(等待池)

(2)、同步阻塞:运行中的线程在获取对象的同步锁时,若当前的对象锁被其他线程所占有,则JVM会将该线程放入锁池中

    (3)、其他阻塞:运行中的线程执行了Thread.sleep()方法 或者join()方法,或者发送了I/O请求时,JVM会将该线程设置为

        阻塞状态

死亡:线程run(),main()方法执行结束,或值run()执行过程中出现异常,则该线程结束生命周期

1. 线程 start 方法执行后,并不表示该线程运行了,而是进入就绪状态,意思是随时准备运行,但是真正何时运

行,是由操作系统决定的,代码并不能控制,

2. 同样的,从运行状态的线程,也可能由于失去了 CPU 资源,回到就绪状态,也是由操作系统决定的。这一步

中,也可以由程序主动失去 CPU 资源,只需调用 yield 方法。

3. 线程运行完毕,或者运行了一半异常了,或者主动调用线程的 stop 方法,那么就进入死亡。死亡的线程不可逆

转。

4. 下面几个行为,会引起线程阻塞。

主动调用 sleep 方法。时间到了会进入就绪状态 主动调用 suspend 方法。主动调用 resume 方法,会进入就绪状态

调用了阻塞式 IO 方法。调用完成后,会进入就绪状态。 试图获取锁。成功的获取锁之后,会进入就绪状态。 线程在

等待某个通知。其它线程发出通知后,会进入就绪状态

41.sleep()  wait() 有什么区别?

1、同步锁zd的对待不同:

sleep()后,程序并不会不释放同步锁。

wait()后,程序会释放同步锁。

2、用法的不同:

sleep()可以用时间指定版来使他自动醒过来。如果时间不到你只能调用interreput()来强行打断。

wait()可以用notify()直接唤起。

(1)sleep()方法是Thread类中的静态方法,wait属于Object基类的成员方法(他们都会是线程进入到阻塞状态)

(2)sleep()方法是线程类(Thread)的方法,不会涉及到线程通信,调用sleep()方法后会使线程睡眠指定的时间,但此时线程不会释放同步锁。wait()涉及到线程通信问题,在调用wait方法后线程会主动释放同步锁.进入等待队列,可以通过notify/notifyAll唤醒线程。才会进入锁池中准备获取对象锁

(3)对象的wait方法即notify()、notifyAll()只能在同步代码块中执行,sleep()能随处调用

(4)sleep()方法必须捕获异常(InterruptedException), wait方法即notify()、notifyAll()则不需要

42.notify() notifyAll()有什么区别?

锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法

(或者synchronized),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该

对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。

等待池:假设一个线程A调用了某个对象的 wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中

如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒

的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由

等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争 优先级高的线程竞争到对象

锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到

等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这

时锁池中的线程会继续竞争该对象锁。

43.线程的 run() start()有什么区别?

调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多

线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。 一个线程对线的 start() 方法只能调用一次,多次调

用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。

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

1newCachedThreadPool(),它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存

线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过60秒,则被终止并移除缓存;长

时间闲置时,这种线程池,不会消耗什么资源。其内部使用SynchronousQueue作为工作队列。

2newFixedThreadPoolint nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队

列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列

中等待空闲线程出现;如果工作线程退出,将会有新的工作线程被创建,以补足指定数目nThreads

3newSingleThreadExecutor(),它的特点在于工作线程数目限制为1,操作一个无界的工作队列,所以它保证了所

有的任务都是被顺序执行,最多会有一个任务处于活动状态,并且不予许使用者改动线程池实例,因此可以避免改变

线程数目。

4newSingleThreadScheduledExecutor()newScheduledThreadPool(int corePoolSize),创建的是个

ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。

5newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部

会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。

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

\1. RUNNING:线程池一旦被创建,就处于 RUNNING 状态,任务数为 0,能够接收新任务,对已排队的任务进行处

理。

\2. SHUTDOWN:不接收新任务,但能处理已排队的任务。调用线程池的 shutdown() 方法,线程池由 RUNNING

变为 SHUTDOWN 状态。

\3. STOP:不接收新任务,不处理已排队的任务,并且会中断正在处理的任务。调用线程池的 shutdownNow()

法,线程池由(RUNNING  SHUTDOWN ) 转变为 STOP 状态。

\4. TIDYING

SHUTDOWN 状态下,任务数为 0, 其他所有任务已终止,线程池会变为 TIDYING 状态,会执行 terminated()

法。线程池中的 terminated() 方法是空实现,可以重写该方法进行相应的处理。

线程池在 SHUTDOWN 状态,任务队列为空且执行中任务为空,线程池就会由 SHUTDOWN 转变为 TIDYING 状态。

线程池在 STOP 状态,线程池中执行中任务为空时,就会由 STOP 转变为 TIDYING 状态。

\5. TERMINATED:线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会由 TIDYING 转变为

TERMINATED 状态。

46.线程池中 submit() execute()方法有什么区别?

submit(Callable task)submit(Runnable task, T result)submit(Runnable task)归属于ExecutorService接口。

execute(Runnable command)归属于Executor接口。ExecutorService继承了Executor

47. java 程序中怎么保证多线程的运行安全?

线程的安全性问题体现在:

原子性:一个或者多个操作在 CPU 执行的过程中不被中断的特性 可见性:一个线程对共享变量的修改,另外一个线

程能够立刻看到 有序性:程序执行的顺序按照代码的先后顺序执行

导致原因:

缓存导致的可见性问题 线程切换带来的原子性问题 编译优化带来的有序性问题解决办法:

JDK Atomic开头的原子类、synchronizedLOCK,可以解决原子性问题 synchronizedvolatileLOCK,可以解决

可见性问题 Happens-Before 规则可以解决有序性问题

48.多线程锁的升级原理是什么?

锁的级别从低到高:

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁

锁分级别原因:

没有优化以前,synchronized是重量级锁(悲观锁),使用 wait  notifynotifyAll 来切换线程状态非常消耗系统

资源;线程的挂起和唤醒间隔很短暂,这样很浪费资源,影响性能。所以 JVM  synchronized 关键字进行了优化,

把锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。

无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失

败的线程会不断重试直到修改成功。

偏向锁:对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续的执行中自动获取锁,降低获取锁带

来的性能开销。偏向锁,指的就是偏向第一个加锁线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争

偏向锁才会被释放。

偏向锁的撤销,需要在某个时间点上没有字节码正在执行时,先暂停拥有偏向锁的线程,然后判断锁对象是否处于被

锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁;

如果线程处于活动状态,升级为轻量级锁的状态。

轻量级锁:轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B

会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。

当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级

锁;当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁。

重量级锁:指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock

现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。

49.什么是死锁?

死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力

作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进

程。

50.怎么防止死锁?

尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLockReentrantReadWriteLock),设置超时时

间,超时可以退出防止死锁。 尽量使用 Java. util. concurrent 并发类代替自己手写锁。 尽量降低锁的使用粒度,尽

量不要几个功能用同一把锁。 尽量减少同步的代码块。

51.ThreadLocal 是什么?有哪些使用场景?

Thread Local类是线程局部变量,是一种实现线程安全的方式。但是在管理环境下使用线程局部变量的时候要特别小

心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没

有释放,Java应用就存在泄露大的风险

52.说一下 synchronized 底层实现原理?

可以保证方法或代码块在运行时,同一时刻只有一个方法可以进入临时界区,同时它还可以保证共享变量的内存可见

普通同步方法,锁是当前实列对象

静态同步方法,锁是当前类的class对象

同步方法块,锁是括号里面的 对象

53.synchronized  volatile 的区别是什么?

volatile本质是告诉jvm当前变量寄存器中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变

量,只有当前线程可以访问该变量,其他线程被阻塞。

volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改和可见性

volatile不会造成线程阻塞;synchronized可能会造成线程阻塞。

volatile标记的变量不会被编译器优化;synchronized可能被编译器优化

54.synchronized  Lock 有什么区别?

首先synchronizedJava内置关键字,在jvm层里面,LOck是个类;

synchronized无法判断是否获取锁的状态,lock可以判断是否获取到锁

synchronized会自动释放锁,否则容易造成线程死锁

synchronized会等待线程,Lock 锁不会等待如果尝试获取不到锁,线程可以不用一直等待就结束了

synchronized锁适合代码少量的同步问题,lock锁适合大量的代码同步问题

55.synchronized

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值