JAVA 基础面试题2024(简洁版)

同系列面试题推荐—— 你的点赞、分享、收藏是对我们最大支持!!!

javaWab面试题(2024简洁版)-CSDN博客

jvm基础面试题2024(简介版)-CSDN博客

(一)基础篇

1、面向对象编程思想

就是把事物看作一个整体,从事物的特征(属性)和行为(方法)两个方面进行描述。

面向对象的过程就是查找对象、建立对象、使用对象、维护对象关系的过程。

2、java的三大特性

  • 封装: 封装是把对象的属性私有化,提供⼀些可以访问属性的⽅法,通过访问这些⽅法得到对象的属性
  • 继承:子类继承父类 。1. ⼦类拥有⽗类对象所有的属性和⽅法(包括私有属性和私有⽅法),但是⽗类中的私有 属性和⽅法⼦类是⽆法访问的,只是拥有。 2. ⼦类可以拥有⾃⼰属性和⽅法,即⼦类可以对⽗类进⾏扩展。 3. ⼦类可以⽤⾃⼰的⽅式实现⽗类的⽅法 。
  • 多态: 多态是同⼀个⾏为具有不同表现形式的能⼒。java中有两种方式可以实现多态,继承父类和实现一个接口。

String类的一些方法

  • length 主要用于字符串和数组、size主要用于集合
  • equals:用于值的比较
  • charAt(),取出某个字符
  • toCharArray:转化为一个字符数组
  • subString 截取字符串
  • split:字符串的拆分为数组
  • replace:把所有的旧的字符串替换
  • indexOf,返回某个字符串的位置
  • toUpperCase(),大写
  • toLowerCase() 小写

3、重载和重写(重点)

重载:同一个类中出现多个方法名相同,但是形参列表不同的情况。返回值的类型和权限修饰符可以不同的( 不能通过返回类型是否相同来判断重载。 )

重写:子类继承父类的方法。方法的方法名、形参列表、返回值类型完全相同,但是逻辑存在差异。

4、接口和抽象类的区别(重点)

  • 抽象类中可以有方法体,但是接口中只有方法的定义(java1.8之后可以定义default方法体);
  • 抽象类只能被继承一个,可以实现多层继承,但是接口可以被实现多个;
  • 抽象类中的属性和方法可以被多种权限修饰符修饰,但是接口中的属性都是由public static finally修饰,方法默认是被public修饰。
  • 抽象类强调所属关系,接口强调特定功能的实现。

5、java中的内部类说一下

分别是成员内部类 、静态内部类、局部内部类、匿名内部类

  • 成员内部类:定义在类的内部
  • 静态内部类: 就是使用static关键字修饰的成员内部类。只能访问外部类的静态成员 ,调⽤静态内部类通过“外部类.静态内部类”
  • 局部内部类:定义在⽅法中的类。
  • 匿名内部类:是指继承⼀个⽗类或者实现⼀个接口的⽅式直接定义并使⽤的类,匿名内部类 没有class关键字,因为匿名内部类直接使⽤new⽣成⼀个对象。

6、访问权限修饰符有哪些?

  • public : 对所有类可见。使用对象:类、接口、变量、方法

  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。

  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

范围大小:

public > protected > default > private

7、字符串存放的位置

  • 如果使⽤常量的⽅式,该对象将被存储在常量池;
  • 如果使⽤new的⽅式,该对象将被存储在堆内存中 。

8、equals和 == 的区别(重点)

  • 对于基本数据类型 == 是比较的值,引用数据类型是比较地址;
  • equals不能直接用于基本数据类型的比较,它是 Object类的方法 ,默认情况下是比较对象的地址,但是我们可以通过重写equals方法来实现对值的比较。

9、字符常量池的作用

是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

10、这句话创建了几个字符串对象?

String s1 = new String("abc");
  • 如果字符串常量池中不存在字符串对象“abc”的引用,那么它将首先在字符串常量池中创建,然后在堆空间中创建,因此将创建总共 2 个字符串对象。
  • 如果字符串常量池中已存在字符串对象“abc”的引用,则只会在堆中创建 1 个字符串对象“abc”。

11、String 类型的变量和常量做“+”运算时发生了什么?

对于String类型的常量相加,编译器会给你优化成两个字符串的拼接,

变量“+” 实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。

12、String、StringBuilder、StringBuffer的区别(重点)

可变性

string由final修饰是不可变的,如果尝试修改会新生成一个字符串对象。而stringBuilder和stringBuffer是可变的;

线程安全

string是不可变的所以线程很安全,stringBuffer的方法加入了synchronized关键字也是安全的;

StringBuilder是线程不安全的;

性能方面

string性能最低,因为他是不可变的,每次修改都会产生新的对象以及内存分配,其次是stringBuffer,它是可变的,不用产生新的对象。但是它要比stringBuilder的性能低,因为stringBuffer加了同步锁会对性能造成影响。

存储方面

string它是可以存储到常量池里面的,而stringBuffer和stringBuilder存储在堆内存里面

数组如何转为集合

Arrays.asList()

13、try-catch-finally 如何使用(重点)

try:用于捕获异常。其后多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。

catch:用于处理 try 捕获到的异常。

finally:无论是否捕获异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。这里我们要注意不能在finally语句中使用return,不然会覆盖try或者catch里面的return。

14、final关键字的作用(重点)

final关键字可以修饰类、⽅法和变量。

  • 当final修饰类的时候,表明这个类不能被继承。final 类中的所有成员⽅法都会被隐式地指 定为 final ⽅法。
  • 当final修饰⽅法的时候,表明这个⽅法不能被重写。
  • 当final修饰变量的时候,如果是基本数据类型的变量,其数值⼀旦在初始化之后便不能更改;如果是引⽤类型的变量,初始化之后便不能再让其指向另⼀个对象

追问:谈谈final、finally、finalize有什么区别?

  • final 可以⽤来修饰类、⽅法、变量。
  • finally 则是 Java 保证重点代码⼀定要被执⾏的⼀种机制。可以使⽤ try-finally 或者 try-catch-finally 来保证重点代码的执行。
  • finalize 是Object 类的⼀个⽅法,它的设计⽬的是保证对象在被垃圾收集前完成特定资源的回收

15、finally 中的代码一定会执行吗?

不一定的!在某些情况下,finally 中的代码不会被执行。

  •  finally 之前虚拟机被终止运行
  • 程序所在的线程死亡。
  • 关闭 CPU。

16、如何使用 try-with-resources 代替try-catch-finally

把资源的创建放在try后面的括号中,程序执行后会进行自动释放。

17、为什么重写 equals() 时必须重写 hashCode() 方法?(重点)

因为两个相等的对象的 hash 值必须是相等。如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hash 值却不相等。

18、那为什么两个对象有相同的 hash 值,它们也不一定是相等的?

因为 hashCode() 所使用的哈希算法也许刚好会让多个对象传回相同的哈希值。越糟糕的哈希算法越容易碰撞,但这也与数据值域分布的特性有关

总结下来就是:

  • 如果两个对象的hash 值相等,那这两个对象不一定相等(哈希碰撞)。

  • 如果两个对象的hash 值相等并且equals()方法也返回 true,我们才认为这两个对象相等。

  • 如果两个对象的hash 值不相等,我们就可以直接认为这两个对象不相等。

19、 String#equals() 和 Object#equals() 有何区别?(重点)

String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Objectequals 方法是比较的对象的内存地址。

20、java中的异常(重点)

java中的异常类是throwable,它由ErrorException两个子类组成。

Error一般指系统的内部错误 ,不能在程序中进行处理。

Excepton指程序中可能出现的异常,分为检查异常运行时异常

运行时检异常是继承了runtimeException,指的是在Java虚拟机正常运⾏期间抛出的异常 ,比如数组越界。

受检异常异常继承了 CheckedException ,提示我们很容易出现异常,要求我们必须进行处理。处理的方式有两种,一种是通过try .. catch 捕捉异常,第二种是通过trow 关键字抛到上一层进行处理。

异常的处理有助于我们很快的定位到异常的位置,同时有利于程序的稳定性和可靠性。

运行时异常:

常见的空指针异常

数组越界异常

类型转换异常

编译时异常

io异常

sql异常

FileNotFoundClassNotFound 异常

21、什么是反射?

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。

22、哪里用到反射机制

  • Idea等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。

  • 很多框架都用到反射机制,注入属性,调用方法,如Spring、 MyBatis 。

  • JDBC中,利用反射动态加载了数据库驱动程序。

  • Web服务器中利用反射调用了Sevlet的服务方法。

23、谈谈反射机制的优缺点

优点:可以让咱们的代码更加灵活、为各种框架的功能提供了便利

缺点:增加了安全问题。比如可以无视泛型参数的安全检查。另外,反射的性能也要稍差点, 总是慢于直接执行java代码 。 ArrayList中的add ()方法,再调用add方法时,就会越过泛型检查

24、java序列化与反序列化

  • 序列化:将数据结构或对象转换成二进制字节流的过程

  • 反序列化:把二进制字节流转换成数据结构或者对象的过程

序列化协议属于 TCP/IP 协议应用层的一部分。

对于不想进行序列化的变量,使用 transient 关键字修饰

25、注解

是 Java5 开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类、方法或者变量,提供某些信息供程序在编译或者运行时使用。

注解本质是一个继承了Annotation 的特殊接口

26、continue、break 和 return 的区别是什么?

  • continue:指跳出当前的这一次循环,继续下一次循环。

  • break:指跳出整个循环体,继续执行循环下面的语句。

return 用于跳出所在方法,结束该方法的运行。return 一般有两种用法:

  • return;:直接使用 return 结束方法执行,用于没有返回值函数的方法

  • return value;:return 一个特定值,用于有返回值函数的方法

27、什么是自动拆装箱?

  • 装箱:将基本类型用它们对应的引用类型包装起来;

  • 拆箱:将包装类型转换为基本数据类型;

28、 如何解决浮点数运算的精度丢失问题?

BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。

BigDecimal a = new BigDecimal("1.0");

(二)集合篇

1、java集合框架有哪些?(重点)

主要包括Collection集合和Map集合。Collection集合是单列集合,下面又包括List集合和Set集合,List集合是有序、可重复。而Set集合是无序、不重复。List集合下面有ArrayList和LinkedList,Set下面有HashSet和TreeSet,HashSet下面有LinkedHashSet。Map集合是双列集合,也是无序、不重复。下面有HashMap和TreeMap,HahMap下有LinkedHashMap。

2、ArrayList和linkedList的区别(重点)

(1)底层实现的原理不同:

arrayList它的底层是动态数组,它通过索引来访问元素,支持快速、随机访问

linkedList它的底层是双向链表,每个元素它都包含前一个元素和后一个元素的引用。适合增删操作。

(2)数据访问的时间复杂度不同

ArrayList的时间复杂度为O(1),而linkedList他需要从头部或尾部进行遍历,所以它的复杂度是O(n)

(3)空间占用方面

ArrayList占用空间是连续的,可能会产生内存碎片,而LinkedList是分散存储,并且占用空间会相对较大,因为它每个结点都包含前后结点的引用。

3、ArrayList的扩容机制(重点)

arrayList它的底层是基于动态数组实现的,当我们创建空数组首次添加元素的时候他会初始化一个长度为10的数组,数组长度不够时候的时候就会触发扩容机制 ,他会创建一个是原来数组长度1.5倍的新数组,并调用 Arrays.copyOf方法 把原来数组的元素拷贝到新的数组中,在进行新元素的添加。

4、HashMap底层实现原理(重点)

在jdk8之前,hashMap的底层由数组+链表进行存储,并且采用头插法。

jdk8之后,hashMap底层采用的是数组+链表+红黑树的结构来存储数据,当链表长度超过8,且数组长度大于等于64的时候,就会把链表转为红黑树,这样就大大提高了查询效率。同时元素插入时会采用尾插法。

5、HashMap扩容机制(重点)

初始化时会创建一个长度为16的数组,默认加载因子是0.75,当数组占用75%时会进行自动扩容,容量变为原来的2倍。将插入元素的hash值与数组长度进行求余,得到存放的index值,如果数组中index位置的值为null,就直接进行插入,如果有值的话先会调用equals()方法比较,不相同的话jdk.18之前会采用头插法的方式进行元素插入。1.8之后会采用尾插法插入。相同的话就会直接进行覆盖。

6、HashMap线程不安全体现在?

在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。

在jdk1.8中,在多线程环境下,会发⽣数据覆盖的情况。

7、Java中线程安全的集合有哪些?(重点)

Vector:就⽐Arraylist多了个同步化机制( 线程安全)。 Stack:栈,也是线程安全的,继承于Vector。

Hashtable:就⽐HashMap多了个线程安全。

CurrentHashMap:是⼀种⾼效且线程安全的集合。

ArrayList 和 Vector 的区别?(了解即可)

  • ArrayListList 的主要实现类,底层使用 Object[]存储,适用于频繁的查找工作,线程不安全 。

  • VectorList 的古老实现类,底层使用Object[] 存储,线程安全。

Vector 和 Stack 的区别?(了解即可)

  • VectorStack 两者都是线程安全的,都是使用 synchronized 关键字进行同步处理。

  • Stack 继承自 Vector,是一个先进后出的栈,而 Vector 是一个动态数组。

随着 Java 并发编程的发展,VectorStack 已经被淘汰,推荐使用并发集合类(例如 ConcurrentHashMapCopyOnWriteArrayList 等)或者手动实现线程安全的方法来提供安全的多线程操作支持

8、ArrayList 可以添加 null 值吗?

ArrayList 中可以存储任何类型的对象,包括 null 值。不过,不建议向ArrayList 中添加 null 值, null 值无意义,会让代码难以维护比如忘记做判空处理就会导致空指针异常。

9、 HashMap和Hashtable的区别?(重点)

(1)线程安全: HashMap 是线程不安全的,HashTable 是线程安全的,因为 HashTable 内部的⽅法基本都经过synchronized修饰。

(2)键值对为null: HashMap可以存储 null 的 key 和 value,但 null 作为键只能有⼀个(转换为0存储),null 作为值可以有多个;HashTable 不允许有 null 键和 null 值, 否则会抛出 NullPointerException。

(3)初始容量⼤⼩和每次扩充容量⼤⼩的不同 :

① 创建时如果不指定容量初始值,Hashtable默认的初始⼤⼩为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化⼤⼩为 16。之后每次扩充,容量变为原来的 2 倍。

② 创建时如果给定了容量初始值,那么 Hashtable 会直接使⽤你给定的⼤⼩,⽽ HashMap 会将其扩充为 2 的幂次⽅⼤⼩。

(4)底层数据结构: JDK1.8 以后的 HashMap当链表长度⼤于8,数组的长度大于等于 64, 将链表转化为红⿊树,以减少搜索 时间。Hashtable 没有这样的机制。

(5)效率: 因为线程安全的问题,HashMap 要⽐ HashTable效率⾼⼀点。另外, HashTable基本被淘汰,不要在代码中使⽤它

10、比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同

都是 Set 接口的实现类,都能保证元素唯一,并且都不是线程安全的。

  • HashSetLinkedHashSetTreeSet 的主要区别在于底层数据结构不同。HashSet 的底层是数组+链表(基于 HashMap 实现)。LinkedHashSet 的底层是双链表+数组,元素的插入和取出是顺序。TreeSet 底层数据结构是红黑树,元素是有序的,排序的方式有自然排序和定制排序。

  • 底层数据结构不同又导致这三者的应用场景不同。HashSet 用于不需要保证元素插入和取出顺序的场景,LinkedHashSet 用于保证元素的插入和取出顺序是有序的,TreeSet 用于支持对元素自定义排序规则的场景

11、HashMap常见遍历方式(重点)

  1. 迭代器(Iterator)方式遍历; map.keySet().iterator();

  2. For Each 方式遍历;

  3. Lambda 表达式遍历(JDK 1.8+);

  4. Streams API 遍历(JDK 1.8+)。

(三)IO流

简述IO流

IO流即Input和Output,Input称为输入流,负责把文件数据读入内存中去,Output称为输出流,负责把内存中的数据写出到磁盘文件或网络中去。

按照流中数据的最小单位还可以分为字节流和字符流。

IO的分类

从编码方式上分为字节流和字符流,字节流常用于图片、音频等文件的复制、转移。而字符流常常用于读写txt、java等文件。同时按照流的方向也可以分为输入流和输出流。所以总的来说可以分为四大流,字节输入流、字节输出流、字符输入流和字符输出流。

字符流和字节流的区别

  1. 数据类型:字节流以字节为单位进行读写操作,而字符流以字符为单位进行读写操作。

  2. 应用场景:字节流主要用于处理二进制数据,如图像、音频、视频文件等。字符流主要用于处理文本数据,如文本文件、配置文件等。

  3. 编码方式:字节流是以原始的字节形式进行读写操作,没有对数据进行任何编码转换。字符流则通过使用指定的字符编码转换为字符,将字符转换为字节再进行读写操作。

  4. 处理效率:由于字符流需要进行字符编码转换,所以在处理大量数据时,字节流的处理效率通常比字符流高。

(四)多线程与并发编程

1、什么是线程和进程

进程: 是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。

线程: 是进程中的一个控制单元,负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。

2、线程与进程的区别

  1. 进程是正在运行程序的实例,进程包含了线程,每个线程执行不同的任务(进程相当于车间,线程相当于车间中的机器人);

  2. 不同的进程使用不同的内存空间,在当前进程下的所有线程可以共享内存空间;

  3. 线程更加轻量,线程的上下文( 寄存器和程序计数器 )切换成本一般要更低;

3、并行与并发有什么区别

并发: 多个作业在同一 时间段 内执行。由于多个作业被快速的切换,给人造成了它们同时执行的感觉。简单来说就是宏观并行、微观串行。

并行: 多个作业在同一 时刻 内执行。

4、同步和异步的区别

  • 同步:发出一个调用之后,如果没有得到结果,就一直等待。

  • 异步:调用在发出之后,不用等待返回结果,该调用直接返回。

5、使用多线程可能带来什么问题?

并发编程的目的就是为了能提高程序的执行效率提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:死锁、线程不安全等。

6、如何理解线程不安全?

在多线程环境下,对于同一份数据,多个线程同时访问时可能会导致数据混乱、错误或者丢失。

7、什么是死锁

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不能正常终止。

8、如何避免死锁?

破坏请求与保持条件:一次性申请所有的资源。

破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

破坏循环等待条件:按某一顺序申请资源,释放资源则反序释放。

9、说一说线程的生命周期和状态(重点)

初始状态,线程被创建出来 。

就绪状态,线程被调用了 start()等待运行的状态。

运行状态,就绪状态时获取到cpu执行权限开始运行。

阻塞状态,需要等待锁释放。

(等待状态,表示该线程需要等待其他线程做出一些特定动作(通知或中断)。

超时等待状态,可以在指定的时间后自行返回而不是像 WAITING 那样一直等待。)

死亡状态,表示该线程已经运行完毕。

10、sleep() 方法和 wait() 方法对比(重点)

共同点:两者都可以暂停线程的执行。

区别

  • wait()法释放了锁,而sleep() 方法没有释放锁 。

  • wait() 通常被用于线程间交互/通信,sleep()通常被用于暂停执行。

  • wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify()或者 notifyAll() 方法。sleep()方法执行完成后,线程会自动苏醒。

  • sleep()Thread 类的静态本地方法,wait() 则是 Object 类的本地方法。

11、可以直接调用 Thread 类的 run 方法吗?(重点)

不可以, 调用 start() 方法 可启动线程并使线程进入就绪状态, 当分配到时间片后就可以开始运行了, start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 直接执行 run() 方法的话不会以多线程的方式执行, 会把 run() 方法当成一个 main 线程下的普通方法去执行。

12、如何保证变量的可见性?volatile 关键字

volatile 关键字可以保证变量的可见性 (但不能保证对变量操作的原子性)。 ,如果我们将变量声明为 volatile ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。

13、乐观锁和悲观锁

悲观锁 :总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题,所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。( 像 Java 中synchronizedReentrantLock等独占锁就是悲观锁思想的实现 )

多用于写比较多的情况,这样可以避免频繁失败和重试影响性能,悲观锁的开销是固定的。

乐观锁:总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源是否被其它线程修改了(具体方法可以使用版本号机制或 CAS 算法)。

写比较少的情况,这样可以避免频繁加锁影响性能。

14、CAS说⼀下

CAS指Compare and swap⽐较和替换,是乐观锁实现的⼀种算法,CAS 指令有三个操作数,变量的内存地址,⽤V表⽰,旧的预期值(⽤A表⽰)和准备设置的新值(⽤B表⽰)。CAS指令在执⾏的时候,只有当v = A,才会⽤B更新V的值,否则它就不会执⾏更新。

15、 synchronized 关键字

synchronized 是 Java 中的一个关键字,是同步的意思,主要解决的是多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

16、synchronized 和 volatile 有什么区别?

synchronized 关键字和 volatile 关键字是两个互补的存在,而不是对立的存在!

  • volatile 关键字是线程同步的轻量级实现,所以 volatile性能肯定比synchronized关键字要好 。但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 。

  • volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。

  • volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。

17、ReentrantLock 是什么?

ReentrantLock 实现了 Lock 接口,是一个可重入且独占式的锁,和 synchronized 关键字类似。不过,ReentrantLock 更灵活、更强大,增加了轮询、超时、中断、公平锁和非公平锁等高级功能。

18、线程同步的几种方式

  • 同步代码块,给发生线程不安全的代码加同步锁,性能比同步方法好,可以少排队一些;

  • 同步方法:在方法后面加上synchronized,可读性更好;

  • Lock锁,更灵活、更强大。

19、Java中创建线程的⽅式有哪些?(重点)

Java中创建线程的⽅式有4种

(1)写⼀个类继承Thread类,重写run⽅法

(2)写⼀个类实现Runnable接口,重写run⽅法

(3)写⼀个实现Callable接口,重写call⽅法

(4)使⽤线程池

线程池

1、什么是线程池

就是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。

2、线程池创建的方式(重点)

  1. 使用ThreadPoolExecutor创建一个线程池对象;

  2. 使用Executors线程池工具类调用方法来进行创建。(不推荐,里面有些方法的最大线程数量或者 队列长度非常大。容易导致oom,即内存溢出)

3、ThreadPoolExecutor七大参数(重点)

  • corePoolSize:线程池核心数量;

  • maximumPoolSize:线程池最大数量;

  • keepAliveTime:临时线程存活时间;

  • unit:临时存活时间的单位;

  • workQueue:任务队列长度;

  • threadFactory:线程池的线程工厂;

  • handler:线程池任务的拒绝策略;

4、线程池的工作流程(重点)

(1)当线程数⼩于或等于核⼼线程数的时候,使⽤核⼼线程数。

(2)如果线程数大于核⼼线程数,就将多余的线程放⼊任务队列(阻塞队列)中

(3)当任务队列(阻塞队列)满的时候,就启动最⼤线程数

(4)当最⼤线程数也达到后,就将启动拒绝策略

5、T1,T2,T3如何保证依次执行(重点)

        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();
  • 使用 Thread.join()
  • CompletableFuture ;
  • 在Executors 类中有个单线程池 ;
  • CountDownLatch

感谢观看!!!

  • 16
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南巷旧人173

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值