java面试题目学习笔记(一)

常见面试题和答案

  1. JDK 和 JRE 有什么区别?
    JVM :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。它只认识 xxx.class 这种类型的文件,它能够将 class 文件中的字节码指令进行识别并调用操作系统向上的 API 完成动作。所以说,jvm 是 Java 能够跨平台的核心,具体的下文会详细说明。
    JRE :英文名称(Java Runtime Environment),我们叫它:Java 运行时环境。它主要包含两个部分,jvm 的标准实现和 Java 的一些基本类库。它相对于 jvm 来说,多出来的是一部分的 Java 类库。
    JDK :英文名称(Java Development Kit),Java 开发工具包。jdk 是整个 Java 开发的核心,它集成了 jre 和一些好用的小工具。例如:javac.exe,java.exe,jar.exe 等。
    显然,这三者的关系是:一层层的嵌套关系。JDK>JRE>JVM。

  2. == 和 equals 的区别是什么?
    (1)==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同
    (2)==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
    (3)==指引用是否相同, equals()指的是值是否相同

  3. 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
    首先,答案肯定是不一定。同时反过来equals为true,hashCode也不一定相同。
    类的hashCode方法和equals方法都可以重写,返回的值完全在于自己定义。
    hashCode()返回该对象的哈希码值;equals()返回两个对象是否相等
    关于hashCode和equal是方法是有一些 常规协定 :
    1、两个对象用equals()比较返回true,那么两个对象的hashCode()方法必须返回相同的结果。
    2、两个对象用equals()比较返回false,不要求hashCode()方法也一定返回不同的值,但是最好返回不同值,亿提搞哈希表性能。
    3、重写equals()方法,必须重写hashCode()方法,以保证equals方法相等时两个对象hashcode返回相同的值。

  4. final 在 java 中有什么作用?
    特征:凡是引用final关键字的地方皆不可修改!
    (1)修饰类:表示该类不能被继承;
    (2)修饰方法:表示方法不能被重写;
    (3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)

  5. java 中的 Math.round(-1.5) 等于多少?
    该方法需要向上舍入,1.5 = 2 -1.5 = -1

  6. String 属于基础的数据类型吗?
    String不是基本的数据类型,是final修饰的java类,java中的基本类型一共有8个,它们分别为:
    1 字符类型:byte 1字节,char 2字节
    2 基本整型:short 2字节,int 4字节,long (32位机 4字节)(64位机)8字节
    3 浮点型:float 4字节,double 8字节
    4 布尔类型:boolean

  7. java 中操作字符串都有哪些类?它们之间有什么区别?
    String、StringBuffer、StringBuilder
    String : final修饰,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。
    StringBuffer : 对字符串的操作的方法都加了synchronized,保证线程安全。
    StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用StringBuilder对象的append、replace、delete等方法修改字符串
    对于操作效率:StringBuilder > StringBuffer > String
    对于线程安全:StringBuffer 是线程安全,可用于多线程;StringBuilder 是非线程安全,用于单线程;不频繁的字符串操作使用 String。反之,StringBuffer 和 StringBuilder 都优于String

  8. String str="i"与 String str=new String(“i”)一样吗?
    不一样的原因很简单,因为他们不是同一个对象。
    String str=“i”;
    这句话的意思是把“i”这个值在内存中的地址赋给str,如果再有String str3=“i”;那么这句话的操作也是把“i”这个值在内存中的地址赋给str3,这两个引用的是同一个地址值,他们两个共享同一个内存。
    而String str2 = new String(“i”);
    则是将new String(“i”);的对象地址赋给str2,需要注意的是这句话是新创建了一个对象。如果再有String str4= new String(“i”);那么相当于又创建了一个新的对象,然后将对象的地址值赋给str4,虽然str2的值和str4的值是相同的,但是他们依然不是同一个对象了。
    需要注意的是:String str=“i”; 因为String 是final类型的,所以“i”应该是在常量池。
    而new String(“i”);则是新建对象放到堆内存中。

  9. 如何将字符串反转?
    第一种 通过String类的charAt()的方法来获取字符串中的每一个字符,然后将其拼接为一个新的字符串。
    第二种 通过String的toCharArray()方法可以获得字符串中的每一个字符串并转换为字符数组,然后用一个空的字符串从后向前一个个的拼接成新的字符串。
    第三种 通过StringBuiler的reverse()的方法,最快的方式

  10. String 类的常用方法都有那些?
    1、public int length()//返回该字符串的长度
    2、public char charAt(int index)//返回字符串中指定位置的字符;注意字符串中第一个字符索引是0,最后一个是length()-1。
    3、提取子串
    1)public String substring(int beginIndex)//该方法从beginIndex位置起,从当前字符串中取出剩余的字符作为一个新的字符串返回。
    2)public String substring(int beginIndex, int endIndex)//该方法从beginIndex位置起,从当前字符串中取出到endIndex-1位置的字符作为一个新的字符串返回。
    4、字符串比较
    1)public int compareTo(String anotherString)//该方法是对字符串内容按字典顺序进行大小比较,通过返回的整数值指明当前字符串与参数字符串的大小关系。若当前对象比参数大则返回正整数,反之返回负整数,相等返回0。
    2)public int compareToIgnore(String anotherString)//与compareTo方法相似,但忽略大小写。
    3)public boolean equals(Object anotherObject)//比较当前字符串和参数字符串,在两个字符串相等的时候返回true,否则返回false。
    4)public boolean equalsIgnoreCase(String anotherString)//与equals方法相似,但忽略大小写。
    5、字符串连接
    public String concat(String str)//将参数中的字符串str连接到当前字符串的后面,效果等价于"+"。
    6、字符串中单个字符查找
    public int indexOf(int ch/String str)//用于查找当前字符串中字符或子串,返回字符或子串在当前字符串中从左边起首次出现的位置,若没有出现则返回-1。
    7、字符串中字符的大小写转换
    1)public String toLowerCase()//返回将当前字符串中所有字符转换成小写后的新串
    2)public String toUpperCase()//返回将当前字符串中所有字符转换成大写后的新串
    8、字符串中字符的替换
    public String replace(char oldChar, char newChar)//用字符newChar替换当前字符串中所有的oldChar字符,并返回一个新的字符串。
    9、其他
    1)String trim()//截去字符串两端的空格,但对于中间的空格不处理。
    2)String[] split(String str)//将str作为分隔符进行字符串分解,分解后的字字符串在字符串数组中返回。

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

  12. 普通类和抽象类有哪些区别?
    1.抽象类不能被实例化。
    2.抽象类可以有构造函数,抽象方法不能被声明为静态。
    3.抽象方法只需申明,而无需实现,抽象类中可以允许普通方法有主体
    4.含有抽象方法的类必须申明为抽象类
    5.抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类。

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

  14. 接口和抽象类有什么区别?
    1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
    2、抽象类要被子类继承,接口要被类实现。
    3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
    4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
    5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
    6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
    7、抽象类里可以没有抽象方法
    8、如果一个类里有抽象方法,那么这个类只能是抽象类
    9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
    10、接口可继承接口,并可多继承接口,但类只能单根继承。

  15. java 中 IO 流分为几种?
    按照流的流向分,可以分为输入流和输出流;
    按照操作单元划分,可以划分为字节流和字符流;
    按照流的角色划分为节点流和处理流。
    Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。
    InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
    OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

  16. BIO、NIO、AIO 有什么区别?
    一:同步阻塞IO(BIO)
    我们熟知的Socket编程就是BIO,一个socket连接一个处理线程(这个线程负责这个Socket连接的一系列数据传输操作)。阻塞的原因在于:操作系统允许的线程数量是有限的,多个socket申请与服务端建立连接时,服务端不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待或被拒绝。
    二:同步非阻塞IO(NIO)
    New IO是对BIO的改进,基于Reactor模型。我们知道,一个socket连接只有在特点时候才会发生数据传输IO操作,大部分时间这个“数据通道”是空闲的,但还是占用着线程。NIO作出的改进就是“一个请求一个线程”,在连接到服务端的众多socket中,只有需要进行IO操作的才能获取服务端的处理线程进行IO。这样就不会因为线程不够用而限制了socket的接入。客户端的socket连接到服务端时,就会在事件分离器注册一个 IO请求事件 和 IO 事件处理器。在该连接发生IO请求时,IO事件处理器就会启动一个线程来处理这个IO请求,不断尝试获取系统的IO的使用权限,一旦成功(即:可以进行IO),则通知这个socket进行IO数据传输。
    三: 异步阻塞IO(AIO)
    NIO是同步的IO,是因为程序需要IO操作时,必须获得了IO权限后亲自进行IO操作才能进行下一步操作。AIO是对NIO的改进(所以AIO又叫NIO.2),它是基于Proactor模型的。每个socket连接在事件分离器注册 IO完成事件 和 IO完成事件处理器。程序需要进行IO时,向分离器发出IO请求并把所用的Buffer区域告知分离器,分离器通知操作系统进行IO操作,操作系统自己不断尝试获取IO权限并进行IO操作(数据保存在Buffer区),操作完成后通知分离器;分离器检测到 IO完成事件,则激活 IO完成事件处理器,处理器会通知程序说“IO已完成”,程序知道后就直接从Buffer区进行数据的读写。

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

  18. java 容器都有哪些?
    数组,String,java.util下的集合容器
    数组长度限制为 Integer.Integer.MAX_VALUE;
    String的长度限制: 底层是char 数组 长度 Integer.MAX_VALUE 线程安全的
    List:存放有序,列表存储,元素可重复
    Set:无序,元素不可重复
    Map:无序,元素可重复

  19. Collection 和 Collections 有什么区别?
    java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
    List,Set,Queue接口都继承Collection。
    java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态方法(对集合的搜索、排序、线程安全化等),大多数方法都是用来处理线性表的。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

  20. List、Set、Map 之间的区别是什么?
    List、Set是实现了Collection接口的子接口;而Map是另一个集合接口;(Collection接口和Map接口是平级的)
    List中的元素,有序、可重复、可为空;
    Set中的元素,无序、不重复、只有一个空元素;
    Map中的元素,无序、键不重,值可重、可一个空键、多可空值;

  21. HashMap 和 Hashtable 有什么区别?
    一、HashMap简介
    HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。
    HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。
    HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。
    二、Hashtable简介
    Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。
    Hashtable也是JDK1.0引入的类,是线程安全的,能用于多线程环境中。
    Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。
    三、HashTable和HashMap区别
    1、继承的父类不同
    Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。
    2、线程安全性不同
    javadoc中关于hashmap的一段描述如下:此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。
    Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。

  22. 如何决定使用 HashMap 还是 TreeMap?
    TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排列的;TreeMap的实现也是基于红黑树结构。
    而HashMap<K,V>的Key值实现散列hashCode(),分布是散列的均匀的,不支持排序,数据结构主要是桶(数组),链表或红黑树。
    所以,查询的时候使用HashMap,增加、快速创建的时候使用TreeMap。

  23. 说一下 HashMap 的实现原理?
    HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。
      首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

  24. 说一下 HashSet 的实现原理?
    HashSet的实现原理总结如下:
    ①是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
    ②当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
    ③HashSet的其他操作都是基于HashMap的

  25. ArrayList 和 LinkedList 的区别是什么?
    1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 ,这里的所谓动态数组并不是那个“ 有多少元素就申请多少空间 ”的意思,通过查看源码,可以发现,这个动态数组是这样实现的,如果没指定数组大小,则申请默认大小为10的数组,当元素个数增加,数组无法存储时,系统会另个申请一个长度为当前长度1.5倍的数组,然后,把之前的数据拷贝到新建的数组。
    2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。,ArrayList是数组,所以,直接定位到相应位置取元素,LinkedLIst是链表,所以需要从前往后遍历。
    3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。//正确,ArrayList的新增和删除就是数组的新增和删除,LinkedList与链表一致。
    4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。,因为ArrayList空间的增长率为1.5倍,所以,最后很可能留下一部分空间是没有用到的,因此,会造成浪费的情况。对于LInkedList的话,由于每个节点都需要额外的指针,所以,你懂的。

  26. 如何实现数组和 List 之间的转换?
    List转数组:toArray(arraylist.size())方法
    数组转List:Arrays的asList(a)方法

  27. ArrayList 和 Vector 的区别是什么?
    (1)同步性:Vector是线程安全的,用synchronized实现线程安全,而ArrayList是线程不安全的,如果只有一个线程会访问到集合,那最好使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们再去考虑和编写线程安全的代码。
    (2)数据容量增长:二者都有一个初始容量大小,采用线性连续存储空间,当存储的元素的个数超过了容量时,就需要增加二者的存储空间,Vector增长原来的一倍,ArrayList增加原来的0.5倍。

  28. Array 和 ArrayList 有何区别?
    可以将 ArrayList想象成一种“会自动扩增容量的Array”。
    Array([]):最高效;但是其容量固定且无法动态改变;
    ArrayList: 容量可动态增长;但牺牲效率;

  29. 在 Queue 中 poll()和 remove()有什么区别?
    Queue 中 remove() 和 poll()都是用来从队列头部删除一个元素。
    在队列元素为空的情况下,remove() 方法会抛出NoSuchElementException异常,poll() 方法只会返回 null 。

  30. 哪些集合类是线程安全的?
    vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
    statck:堆栈类,先进后出
    hashtable:就比hashmap多了个线程安全
    enumeration:枚举,相当于迭代器

  31. 迭代器 Iterator 是什么?
    迭代器就是专门取出集合元素的对象。但是该对象比较特殊,不能直接创建对象(通过new),该对象是以内部类的形式存在于每个集合类的内部。
    正是由于每一个容器都有取出元素的功能。这些功能定义都一样,只不过实现的具体方式不同(因为每一个容器的数据结构不一样)所以对共性的取出功能进行了抽取,从而出现了Iterator接口。而每一个容器都在其内部对该接口进行了内部类的实现。也就是将取出方式的细节进行封装。

  32. Iterator 怎么使用?有什么特点?
    Iterator iterator = list.iterator();
    while (iterator.hasNext()) {
    String str = iterator.next();
    System.out.println(str);
    }
    Java中使用Iterator来遍历集合元素,Iterator遍历集合元素有以下几个特点:
    Iterator遍历集合元素的过程中不允许线程对集合元素进行修改,否则会抛出ConcurrentModificationEception的异常。
    Iterator遍历集合元素的过程中可以通过remove方法来移除集合中的元素。
    Iterator必须依附某个Collection对象而存在,Iterator本身不具有装载数据对象的功能。
    Iterator.remove方法删除的是上一次Iterator.next()方法返回的对象。
    强调以下next()方法,该方法通过游标指向的形式返回Iterator下一个元素。

  33. Iterator 和 ListIterator 有什么区别?
    1.ListIterator有add()方法,可以向List中添加对象,而Iterator不能
    2.ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。
    3.ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
    4.都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。
    因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。其实,数组对象也可以用迭代器来实现。

  34. 怎么确保一个集合不能被修改?
    采用Collections包下的unmodifiableMap方法
    Collections.unmodifiableList(List)
    Collections.unmodifiableSet(Set)

  35. 并行和并发有什么区别?
    并发(concurrency)和并行(parallellism)是:
    解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
    解释二:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
    解释三:在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群
    所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能

  36. .线程和进程的区别?
    进程是执行着的应用程序,线程是进程中的执行单元。
    进程是资源分配单元,线程是执行单元
    进程之间相互独立,同一进程的线程共用进程资源。
    进程间通讯通过IPC,同一进程间线程通讯通过写入进程数据段来通讯,需要用到sychronized与voltaile等线程同步手段保持数据的一致性。
    线程切换比进程切换快而且所需资源较少。

  37. 守护线程是什么?
    守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程,这是它的作用——而其他的线程只有一种,那就是用户线程。所以java里线程分2种,守护线程,比如垃圾回收线程,就是最典型的守护线程、用户线程,就是应用程序里的自定义线程

  38. 创建线程有哪几种方式?
    继承Thread类创建线程类
    (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
    (2)创建Thread子类的实例,即创建了线程对象。
    (3)调用线程对象的start()方法来启动该线程。
    通过Runnable接口创建线程类
    (1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
    (2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
    (3)调用线程对象的start()方法来启动该线程。
    通过Callable创建线程
    (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

  39. 说一下 runnable 和 callable 有什么区别?
    相同点:
    两者都是接口;(废话)
    两者都可用来编写多线程程序;
    两者都需要调用Thread.start()启动线程;
    不同点:
    两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
    Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

  40. 线程有哪些状态?
    1.新建(NEW):新创建了一个线程对象。
    2.可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
    3.运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
    4.阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
    (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
    (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
    (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
    5.死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

  41. sleep() 和 wait() 有什么区别?
    1、这两个方法来自不同的类分别是Thread和Object,sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。
    2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
    3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,使用notify或notifyAll才能唤醒,而sleep可以在任何地方使用(使用范围)。

  42. notify()和 notifyAll()有什么区别?
    如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
    当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
    优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁

  43. 线程的 run()和 start()有什么区别?
    调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
    一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。

  44. 创建线程池有哪几种方式?
    newFixedThreadPool
    定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
    newCachedThreadPool
    可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
    newScheduledThreadPool
    定长线程池,可执行周期性的任务
    newSingleThreadExecutor
    单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
    newSingleThreadScheduledExecutor
    单线程可执行周期性任务的线程池
    newWorkStealingPool
    任务窃取线程池,不保证执行顺序,适合任务耗时差异较大。
    线程池中有多个线程队列,有的线程队列中有大量的比较耗时的任务堆积,而有的线程队列却是空的,就存在有的线程处于饥饿状态,当一个线程处于饥饿状态时,它就会去其它的线程队列中窃取任务。解决饥饿导致的效率问题。
    默认创建的并行 level 是 CPU 的核数。主线程结束,即使线程池有任务也会立即停止。

  45. 线程池都有哪些状态?
    线程池的5种状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。
    线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
    线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
    线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
    当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()
    线程池彻底终止,就变成TERMINATED状态。

  46. 线程池中 submit()和 execute()方法有什么区别?
    线程池中的execute方法大家都不陌生,即开启线程执行池中的任务。还有一个方法submit也可以做到,它的功能是提交指定的任务去执行并且返回Future对象,即执行的结果。下面简要介绍一下两者的三个区别:
    1、接收的参数不一样,submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)
    2、submit有返回值,而execute没有
    用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。
    然后我就可以把所有失败的原因综合起来发给调用者。
    个人觉得cancel execution这个用处不大,很少有需要去取消执行的。
    而最大的用处应该是第二点。
    3、submit方便Exception处理
    意思就是如果你在你的task里会抛出checked或者unchecked exception,
    而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。

  47. 在 java 程序中怎么保证多线程的运行安全?
    线程的安全性问题体现在:
    原子性:一个或者多个操作在 CPU 执行的过程中不被中断的特性
    可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到
    有序性:程序执行的顺序按照代码的先后顺序执行
    导致原因:
    缓存导致的可见性问题
    线程切换带来的原子性问题
    编译优化带来的有序性问题
    解决办法:
    JDK Atomic开头的原子类、synchronized、LOCK,可以解决原子性问题
    synchronized、volatile、LOCK,可以解决可见性问题
    Happens-Before 规则可以解决有序性问题

  48. 多线程锁的升级原理是什么?
    锁的级别:
    无锁->偏向锁->轻量级锁->重量级锁
    锁分级别原因:
    没有优化前,sychroniezed是重量级锁(悲观锁),使用wait、notify、notifyAll来切换线程状态非常消耗系统资源,线程的挂起和唤醒间隔很短暂,这样很浪费资源,影响性能。所以JVM对sychronized关键字进行了优化,把锁分为无锁、偏向锁、轻量级锁、重量级锁,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。

  49. 什么是死锁?
    死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
    当然死锁的产生是必须要满足一些特定条件的:
    1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
    2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
    3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
    4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

  50. 怎么防止死锁?
    尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
    尽量使用 Java. util. concurrent 并发类代替自己手写锁。
    尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
    尽量减少同步的代码块

  51. ThreadLocal 是什么?有哪些使用场景?
    ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。
    经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

  52. 说一下 synchronized 底层实现原理?
    synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性

  53. synchronized 和 volatile 的区别是什么?
    synchronized关键字解决的是执行控制的问题,被synchronized关键字保护的代码块无法被其它线程访问,内存屏障指令保证了所有CPU操作结果都会直接刷到主存中,从而保证了操作的内存可见性。
    volatile关键字解决的是内存可见性的问题,会使得所有对volatile变量的读写都会直接刷到主存,即保证了变量的可见性。
    区别:
    volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
    volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
    volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
    volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
    volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

  54. synchronized 和 Lock 有什么区别?
    两者区别:
    1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
    2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
    3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
    4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
    5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
    6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值