1. 面向对象

  • 封装

把对象的属性和行为结合为一个独立的整体,并尽可能地隐藏对象内部的实现细节;就是把不想告诉或者不该告诉别人的东西隐藏起来,把可以告诉别人的公开,别人只能用我们提供的功能实现需求,而并不知道怎么实现的,增加安全性。

  • 继承

子类继承父类的属性和行为,并能根据自己的需求扩展出新的行为,提高了代码的复用性。

  • 多态

指允许不同对象对同一消息做出响应,即同一消息可以根据发送对象不同而采取不同的行为方式(发送消息即函数调用)。封装和继承都是为多态做准备的,在执行期间判断引用对象的实际类型,根据其实际类型调用其相应方法。

  • 抽象

表示对问题领域进行分析、设计中得出来的抽象的概念,是对一系列看上去不同,但其本质相同的具体概念的抽象。在 Java 中抽象用 abstract 关键字修饰,此类不能被实例化,由此看出抽象类(接口)就是为了继承而存在。

2. Java 基本数据类型

  数据类型 包装类 字节数 位数
字节型 byte Byte 1 8
短整型 short Short 2 16
整型 int Integer 4 32
长整型 long Long 8 64
单精度浮点数 float Float 4 32
双精度浮点数 double Double 8 64
字符型 char Character 2 16
布尔型 boolean Boolean 1 8

3. JDK、JRE、JVM 区别

  • JDK(Java Development Kit)

是整个 Java 核心,是 Java 开发工具包,包括 Java 运行环境、Java 工具和 Java 基础类库。

  • JRE(Java Runtime Environment)

是 Java 程序所必须的环境集合,包含 Java 虚拟机和 Java 一些核心类库。

  • JVM(Java Virtual Machine)

Java 虚拟机,是 Java 实现跨平台最核心的部分,能够运行以 Java 语言编写的程序。

4. 重载和重写的区别

  • 重载

发生在同一类中,方法名必须相同,参数类型、数量、顺序不同,方法返回值和修饰符可以不同,发生在编译时。

  • 重写

发生在父子类中,方法名、参数列表必须相同,返回值范围、抛出异常范围小于等于父类,访问修饰符范围大于等于父类,如果父类访问修饰符为 private 则子类不能重写。

5. == 和 equals() 的区别

  • ==

基本类型比较的是值是否相等,引用类型比较的是地址值是否相等。

  • equals()

引用类型默认比较地址值是否相等,重写该方法则比较的是值是否相等。

6. String、StringBuilder、StringBuffer 区别

  • String 字符串常量

String 类中使用 final 数组保存字符串(private final char value[]),String 对象是不可变的,可以理解为常量,线程安全。

  • StringBuilder 字符串变量(非线程安全)

没有对方法加同步锁,非线程安全。

  • StringBuffer 字符串变量(线程安全)

对方法或调用的方法加了同步锁,线程安全。

小结

  • 操作少量数据用 String。
  • 单线程操作字符串缓冲区下大量数据用 StringBuilder。
  • 多线程操作字符串缓冲区下大量数据用 StringBuffer。

7. 接口和抽象类的区别

  • 抽象类通过 extends 继承,接口通过 implements 实现。
  • 抽象类可以有构造函数,接口反之。
  • 抽象类可以有 main 方法且能运行,接口反之。
  • 一个类只能继承一个抽象类,但能实现多个接口。
  • 抽象类可以使用任意访问修饰符,接口默认使用 public。

8. String 常用方法

  • indexOf():返回指定字符索引
  • charAt():返回指定索引处字符
  • replace():字符串替换
  • trim():去除字符串两端空白
  • split():分割字符串,返回一个字符串数组
  • getBytes():返回字符串 byte 类型数组
  • length():返回字符串长度
  • toUpperCase():字符串转大写
  • toLowerCase():字符串转小写
  • substring():字符串截取
  • equals():字符串比较

9. 单例模式

某个类的实例在多线程环境下只会被创建一次。

  • 懒汉式

线程安全,延迟初始化。

public class Singleton {
    private static Singleton instance;
    private Singleton(){}
    
    public static Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 饿汉式

线程安全,一开始就初始化。

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}
  • 双检索

线程安全,延迟初始化。

public class Singleton {
    private volatile static Singleton instance;
    private Singleton(){}

    public static Singleton getInstance(){
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

10. 反射

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

获取 class 类三种方法:

Object o = new Object();
// getClass
Class clazz = o.getClass();
// 对象.class
Class clazz = o.class;
// Class.forName()
Class clazz = Class.forName("类的全路径");

11. Java 8 新特性

  • Lambda 表达式

Lambda 允许把函数作为一个方法的参数。

new Thread(() -> System.out.println("AAA")).start();

  • 方法引用

允许直接引用已有 Java 类或对象的方法、构造方法。

ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.forEach(System.out::println);
  • 函数式接口

有且仅有一个抽象方法的接口称为函数式接口,可以隐式地转换为 Lambda 表达式。通常函数式接口上会添加 @FunctionalInterface 注解。

  • 接口允许定义默认方法和静态方法

JDK 8 开始,接口允许存在一个或多个默认方法和静态方法。

  • Stream API

把真正的函数式编程风格引入到 Java 中,这种风格将要处理的元素集合看作一种流,流在管道里传输,并可以在管道的节点上进行处理,如筛选、排序、聚合等。

List<String> list = Arrays.asList("A","B","B","C","");
list.stream()
    .filter(s -> !s.isEmpty()) // 过滤空字符串
    .distinct() // 去重
    .forEach(s -> System.out.println(s)); // 遍历
  • 时间日期类改进

新增 java/time 包,LocalDate/LocalTime/LocalDateTime。

  • Optional 类

Optional 类对象是一个可以为 null 的容器对象,如果值存在则 isPresent() 方法会返回 true ,调用 get() 方法会返回该对象。

String a = "AAA";
Optional<String> optional = Optional.of(a);
boolean present = optional.isPresent();
if (present) {
    String s = optional.get();
    System.out.println(s);
}
  • Base 64 实现

 内置了 Base 64 编码的编码器和解码器。

12. Java 异常

Throwable 是所有 Java 程序中错误处理的父类,由两种子类:Error 和 Exception。

  • Error

表示 JVM 所检测到无法预期的错误,由于这是 JVM 层次的严重错误,导致 JVM 无法继续运行,因此这种错误无法捕捉和采取任何恢复操作,只能展示错误信息。

  • Exception

可以捕捉和处理的异常。

    • 运行时异常

都是 RuntimeException 类及其子类异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不进行处理。这些异常一般由程序的逻辑错误引起的,程序应从逻辑上尽可能避免这种错误发生。运行时异常的特点是 Java 编译器不会检查它,也就是说即使没有用 try-catch 捕获或者 throw 抛出异常,也会编译通过。  

    • 非运行时异常

是 RuntimeException 以外的异常,类型上属于 Exception 类及其子类。从程序语法讲是必须要处理的异常,如果不处理,程序就不能编译通过。如 IOException、SQLException。

常见的 RuntimeException :

  • NullPointerException:空指针异常
  • ClassCastException:类型转换异常
  • IllegalArgumentException:传递参数非法异常
  • ArithmeticException:算数运算异常
  • ArrayStoreException:数组存放数据与声明数据类型不兼容异常
  • IndexOutOfBoundsException:数组下标越界异常
  • NegativeArraySizeException:数组大小为负数异常
  • NumberFormatException:数字格式异常
  • SecurityException:安全异常
  • UnsupportedOperationException:不支持的操作异常

13. BIO、NIO、AIO区别

  • BIO

Block IO 同步阻塞式 IO,就是平常使用的传统 IO,特点是模式简单使用方便,并发处理能力低。

  • NIO

New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务端通过 Channel(通道)通讯,实现了多路复用。

  • AIO

Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非阻塞 IO,基于事件和回调机制。

14. ThreadLocal 原理

为共享变量在每个线程创建一个副本,每个线程可以访问自己的副本变量,通过 ThreadLocal 保证线程安全性。其实在 ThreadLocal 类中有一个静态内部类 ThreadLocalMap(类似于 Map),用键值对形式存储每个线程变量副本,ThreadLocalMap 中元素的 key 为当前 ThreadLocal 对象,value 为对应线程的变量副本。ThreadLocal 本身并不存储值,它只是作为一个 key 存储到 ThreadLocalMap 中,用的是弱引用,在 GC 的时候可能会被回收,这样 ThreadLocalMap 中会存在一些 key 为 null 的键值对(Entry)。因为 key 为 null ,所以这些 Entry 无法进行访问,但是 Entry 本身也不会被清除。如果没有手动删除对应 key 就会导致这块内存不会回收也不能访问,造成内存泄漏。

所以使用完 ThreadLocal 后,调用 remove() 方法。在不使用线程池的情况下,即使不调用 remove() 方法,线程的变量副本也会被 GC 回收,从而不会造成内存泄漏。

15. 同步锁、死锁、乐观锁、悲观锁

  • 同步锁

并发执行多个线程,在同一时间只允许一个线程访问共享数据。Java 可以使用 synchronized 关键字来取得一个对象的同步锁。

  • 死锁

多个线程同时被阻塞,它们中一个或全部都在等待某个资源释放。

  • 乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别的线程不会更改,所以不会上锁;但是在更新的时候会判断一下在此期间别的线程有没有更新这个数据,可以通过版本号机制和 CAS 算法实现。

  • 悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别的线程会修改,所以每次拿数据前都会上锁,这样别的线程想拿数据就会阻塞直到拿到锁。

16. synchronized 底层实现原理

synchronized 可以保证方法和代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时还保证共享变量的内存可见性。Java 中每个对象都可以作为锁,这是 synchronized 实现同步的基础。普通同步方法,锁的是当前实例对象;静态同步方法,锁的是当前类的 class 对象;同步代码块,锁的是代码块里面的内容。

17. synchronized 和 volatile 区别

  • volatile 本质是在告诉 JVM 当前变量在寄存器中的值是不确定的,需要从主存中读取;synchronized 则是锁定当前变量,只有当前线程可以访问此变量,其他线程被阻塞。
  • volatile 仅能使用在变量级别;synchronized  可以使用在变量、方法、类级别。
  • volatile 能实现变量修改的可见性,不能实现变量的原子性;synchronized 都能实现。
  • volatile 不会造成线程阻塞;synchronized 反之。
  • volatile 标记的变量不会被编译器优化;synchronized 反之。

18. synchronized 和 Lock 区别

  • synchronized 是个关键字;Lock 是个 Java 类。
  • synchronized 无法判断是否获取锁;Lock 可以。
  • synchronized 会在执行完毕或发生异常自动释放锁;Lock 需要在 finally 中通过 unlock() 方法手动释放锁,否则容易造成死锁。
  • synchronized 中即使获取锁的线程阻塞,另一个线程会一直等待;Lock 中如果线程尝试获取不到锁,线程会自动结束。
  • synchronized 锁可重入、不可中断、非公平;Lock 锁可重入、可中断、可公平。
  • synchronized 锁适合少量代码同步;Lock 反之。

19. 冒泡排序

public class Sort {

    public static void main(String[] args) {
        sort();
    }

    public static void sort(){
        Scanner sc = new Scanner(System.in);
        int[] sort = new int[10];
        int temp;
        System.out.println("请输入要排序的数据:");
        for (int i = 0; i < sort.length; i++) {
            sort[i] = sc.nextInt();
        }

        for (int i = 0; i < sort.length - 1; i++) {
            for (int j = 0; j < sort.length - i - 1; j++) {
                if (sort[j] > sort[j + 1]) {
                    temp = sort[j];
                    sort[j] = sort[j + 1];
                    sort[j + 1] = temp;
                }
            }
        }

        System.out.println("排序后的顺序为:");
        System.out.println(Arrays.toString(sort));

    }
}

20.  JDK 1.8 JVM 内存

  • 程序计数器

线程私有,用来记录代码执行位置

  • Java 虚拟机栈

线程私有,方法执行会创建一个栈帧,栈帧入栈执行,执行完毕出栈

  • 本地方法栈

线程私有,与虚拟机栈类似,虚拟机栈加载的是普通方法,本地方法栈加载的是 native 修饰的方法

线程共享,用于存放 new 出来的对象以及常量池

  • 元空间

存储类、方法、静态变量等信息

JDK 1.8 和 1.7 最大的区别就是 1.8 将方法区去除,在本地内存中新加入元空间。

21. JDK 1.8 堆内存结构

  • Young(新生代

新创建的对象都在新生代,内存按照 8:1:1 分为一个 Eden 区和两个 Survivor(S0 和 S1)区。大部分对象在 Eden 区中生成,回收时将 Eden 区存活对象复制到 S0,当 S0 存满时,将 Eden 区和 S0 中存活对象复制到 S1 中,然后清空 Eden 区和 S0,最后将 S0 和 S1 交换,即保持 S1 为空,如此往复。当 Eden 区和 S0 中存活数据大于 S1 容量时,就会将所有存活对象放入老年代中,当老年代存满时,将会触发 Full GC,也就是新生代、老年代都进行一次回收。新生代发生的 GC 也叫 Minor GC,触发频率比较高,不一定要等 Eden 区满了才触发。

  • Tenured(老年代)

新生代经过多次垃圾回收存活下的对象存在老年代,老年代中存放的都是一些生命周期比较长的对象。内存也比新生代大很多,大概 2 倍,当老年代内存满了时触发 Major GC 即 Full GC,发生频率比较低,因为对象存活时间比较长,存活率比较高。

  • Permanent(永久代)

用于存放静态文件,如 Java 类、方法等,对垃圾回收没有显著影响。JDK 1.8 将永久代去除,将永久代数据放入元空间。

22. GC 垃圾回收

JVM 垃圾回收大致分为两步,垃圾发现与垃圾回收;线程私有不存在垃圾回收,所以堆中存在垃圾回收。

垃圾发现

  • 引用计数算法

堆中对象每引用一次计数器 +1,取消引用计数器 -1,当计数器为 0 时进行垃圾回收;缺点是无法解决对象循环引用问题。

  • 可达性分析算法

将所有引用关系看作是一张图,从根节点 GC ROOT 开始向下搜索,寻找引用的节点,当所有引用节点搜索完毕,剩下的节点将当作垃圾清除。

可以作为 GC ROOT 的对象

    • 虚拟机栈中引用的对象
    • 本地方法栈中引用的对象
    • 方法区中静态变量引用的对象
    • 方法去中常量引用的对象

垃圾回收

  • 标记-清除算法

首先标记所有需要清除的对象,全部标记完后统一清除;效率不高,会产生大量不连续内存碎片。

  • 标记-整理算法

是标记-清除的改进,标记完让存活对象向一端移动,在移动过程清除掉可回收对象。

  • 复制算法

按内存容量分成大小相等的两块,每次只使用其中一块,当这块内存满了时,将存活对象放入另一块中,然后清除剩下的对象。

  • 分代收集算法

按对象的存活周期将内存分为新生代、老年代和永久代,不同的生命周期采取不同的回收算法,提高回收效率。

23. JVM 调优参数

  • -Xmx3550m:设置 JVM 最大可用内存为 3550M
  • -Xms3550m:设置 JVM 初始内存为 3550M,一般设置与 -Xmx 相同,避免垃圾回收后 JVM 重新分配内存
  • -Xmn2g:设置新生代内存大小为 2G,JVM 内存大小 = 新生代 + 老年代 + 永久代,此值对系统影响较大,Sun 公司推荐配置为整个堆的 3/8
  • -Xss256k:设置每个线程的栈大小。JDK 5 以后每个线程栈大小为 1M,以前为 256k
  • -XX:NewRatio=4:设置新生代与老年代的比例为 1:4,默认是 1:2
  • -XX:SurvivorRatio=4:设置新生代 Eden 区和两个 Survivor 的比例为 4:2

24. 常见数据结构

  • 数组

最常见的数据结构,长度固定,无法扩容,只能存储一种类型数据,新增、删除慢,因为要移动其他数据。

先进后出的数据结构,只能在一端操作的特殊线性表,先入栈的数据后执行,后入栈的数据先执行。

  • 队列

先进先出的数据结构,只能在一端进行插入,另一端进行删除的特殊线性表,先进入的数据会被先读取。

  • 链表

链表是一种物理存储单元上非连续、非顺序的存储结构,其物理结构不能只表示数据元素的逻辑顺序,是由链表中的指针链接次序来实现的;链表是由一系列结点组成,结点可以在运行时动态生成,根据指针的指向,链表能生成不同的结构,如单链表、双向链表、循环列表等。

是计算机中非常重要的一种数据结构,能描述家谱、单位组织架构等等;有二叉树、平衡树、红黑树、B 树、B+ 树。

  • 散列

也称哈希,根据键和值直接访问的数据结构。

通常可以被看作是一棵完全二叉树的数组对象。

由一顶点和一组能将两个顶点相连的边组成。

25. 集合和数组的区别

  • 数组长度固定;集合长度可变。
  • 数组只能存储一种类型数据;集合能存储对象。

26. List、Map、Set区别

List 和 Set 是存储单列数据集合;Map 是存储键值这样双列数据集合。

  • List 有序,值可重复
  • Map 无序,键不允许重复,值可以重复
  • Set 无序,不可重复,元素位置由元素的 hashcode 决定

27. 集合实现类

Collection 接口

  • List
    • ArrayList
    • 底层结构是数组,查询块,增删慢,线程不安全,效率高。
    • Vector(已舍弃)
    • 底层结构是数组,查询块,增删慢,线程不安全,效率高。 
    • LinkedList
    • 底层结构是链表,查询慢,增删块,线程不安全,效率高。
  • Set
    • HashSet
    • 底层数据结构是哈希表,无序,不可重复,通过 hashCode() 和 equals() 保证元素唯一 。
    • LinkedHashSet
    • 底层数据结构是链表和哈希表,有序,不可重复,通过链表实现有序,哈希表实现唯一。 
    • TreeSet
    • 底层数据结构是红黑树,有序,不可重复,通过自然排序、比较器排序,再根据比较返回值是否为 0 来实现唯一。 

Map

  • HashMap

基于 hash 表的 Map 接口实现,键和值允许为 null ,线程不安全,效率高。 

  • HashTable

键和值不允许为  null, 线程安全,效率低。

  • LinkedHashMap

是 HashMap 一个子类,有序,线程安全,效率低。 

  • TreeMap

默认按键值升序排序,线程不安全,效率高

28. HashMap 底层原理

JDK 1.8 之前由数组 + 链表组成,之后由数组 + 链表 + 红黑树组成,提高了查询效率,当链表长度超过 8 自动转为红黑树,小于 6 自动转为链表。

当 new HashMap() 底层没有创建数组,首次调用 put() 方法时,底层会创建长度为 16 的数组,JDK 8 底层数组是 Node[] 而非 Entry[],用数组容量大小乘以加载因子得到一个值,一旦数组元素个数超过该值就会调用 rehash() 将数组容量扩大 2 倍,称为扩容;扩容时会生成一个新数组,需要将原来数组元素重新计算 hash 值放入新数组中,非常消耗性能。

HashMap 默认长度为 16,加载因子默认 0.75,当长度超过 12 自动扩容。

Java 中任何对象都有哈希值,哈希算法就是通过哈希值与自己行向右位移 16 的异或运算,是为了计算出来的值足够随机,足够分散,产生的数组下标足够随机。

put 原理

首先将键和值封装到 Node 对象中,调用键的 hashCode() 方法获取哈希值,并通过哈希算法转化成数组下标,下标位置如果没有元素,就把 Node 对象添加到这个位置,如果存在元素,就用键与节点上键进行 equals(),如果返回 false 就将 Node 对象添加到链表末尾,如果返回  ture 就将此节点值覆盖。

get 原理

首先调用键的 hashCode() 方法获取哈希值,通过哈希算法转换成数组下标,下标位置如果没有元素直接返回 null,如果存在元素,就用键与节点上的键进行 equals(),如果返回 false 直接返回 null,如果返回 true 就将此节点值取出返回。

29. HashMap 与 HashTable 区别

  • HashMap 线程不安全;HashTable 线程安全。
  • HashMap 允许键值为 null;HashTable 不允许。
  • HashMap 效率比 HashTable 高。
  • HashTable 是同步的。

HashTable 是遗留类,内部实现很多没优化和冗余,即使在多线程环境下,有同步的 ConcurrentHashMap 替代。

30. HashTable 与 ConcurrentHashMap 区别

HashTable 使用 Synchronized 关键字修饰;ConcurrentHashMap 在 JDK 1.7 使用分段锁保证线程安全,JDK 1.8 取消了 Segment 分段锁,使用 CAS 和 Synchronized 保证并发线程安全,数据结构为数组 + 链表 + 红黑树。

Synchronized 只锁定当前链表或红黑树首节点,只要不 hash 冲突,就不会产生并发,效率提升 N 倍。

31. 线程和进程区别

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

  • 线程

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

  • 进程

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

32. 创建线程方式

  • 继承 Thread 类重写 run(),简单单一,不能继承其他类。
  • 实现 Runnable 接口重写 run(),避免单继承局限性,灵活解耦。
  • 实现 Callable 接口重写 call(),可以获取返回值,可以抛出异常。
  • 线程池创建,Executor 接口。

33. Runnable 和 Callable 区别

  • Runnable 无返回值;Callable 有返回值,支持泛型。
  • Runnable 只能抛出运行时异常,且不能捕获;Callable 反之。

34. start() 和 run() 区别

start() 可以启动线程,使线程进入就绪状态;run() 只是 Thread 的一个普通方法,在主线程中执行。

35. 线程的状态

  • 新建

生成线程对象

  • 就绪

调用了 start()

  • 运行

将就绪状态的线程设为当前线程,运行 run()

  • 阻塞

线程因为某种原因放弃 cpu 使用权,暂时停止运行,直到线程重新进入到就绪状态,才有机会转到运行状态。

    • 等待
    • 调用 wait() 使线程等待某工作完成 
    • 超时等待
    • 调用 sleep() 或 join() 或发出了 I/O 请求,线程进入阻塞状态,当 sleep() 状态超时、join() 等待线程终止或超时、I/O 处理完毕,线程重新进入就绪状态。 
    • 同步阻塞
    • 线程获取 synchronized 同步锁失败 
  • 死亡

线程执行完毕或异常退出,线程结束生命周期

36. 线程相关方法

  • 线程等待(wait)

调用该方法的线程进入 waiting 状态,等待其他线程通知或被中断才会返回,会释放锁,一般用于同步方法或同步代码块中。

  • 线程睡眠(sleep)

导致线程进入 time-waiting 状态,不会释放当前持有的锁。

  • 线程让步(yield)

使当前线程让出 cpu 执行权,与其他线程重新竞争。

  • 线程中断(interrupt)

本意是给线程一个中断的信号,会影响线程内部的一个中断标识位,线程本身并不会因此而改变状态。

  • 线程加入(join)

使线程执行完 run() 方法后,再执行 join() 方法后面的代码。

  • 线程唤醒(notify)

随机唤醒等待的单个线程,notifyAll() 唤醒所有线程。

37. wait() 和 sleep() 区别

  • wait() 来自 Object 类;sleep() 来自 Thread 类。
  • wait() 会释放锁;sleep() 不会。
  • wait() 必须在同步方法或同步代码块中使用;sleep() 任何地方可用。
  • wait() 不需要捕获异常;sleep() 需要。

38. 线程池优点

  • 使用线程池可以重复使用已有线程,避免了线程在创建和销毁所造成的损耗
  • 没有创建和销毁的损耗,提高了系统响应速度
  • 对线程进行管理,如根据系统承受能力调整可运行线程数量大小等

39. 线程池分类

  • newCachedThreadPool

创建一个带缓存可重复利用的线程池

  • newFixedThreadPool

创建一个可重用、固定线程数量的线程池

  • newSingleThreadExecutor

创建一个单线程的线程池

  • newScheduledThreadPool

创建一个线程池,支持延时和定期执行

40. 线程池的核心参数

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲线程存活时间
  • unit:时间单位
  • workQueue:阻塞队列
  • threadFactory:创建线程的工程
  • handler:拒绝策略

41. 线程池原理

当一个任务提交至线程池,首先判断核心线程数是否已满,没有则创建线程执行任务;否则放入阻塞队列,如果阻塞队列已满,判断最大线程数是否已满,没有则创建线程执行;否则将执行拒绝策略。

42. 拒绝策略

  • AbortPolicy

系统默认,丢弃任务并抛 RejectedExecutionException 异常

  • DiscardPolicy

丢弃任务,不抛异常,可能造成数据丢失

  • DiscardOldestPolicy

丢弃最前面的任务(通常是存活时间最长的任务),并尝试重试执行,也有数据丢失风险

  • CallerRunsPolicy

既不丢弃任务,也不抛异常,而是将某些任务回退给调用者,让调用者去执行

43. 线程池关闭

  • shutdownNow

首先将线程池状态设置为 stop,然后尝试停止所有正在执行和未执行的线程,并返回等待执行任务列表

  • shutdown

将线程池状态设置为 shutdown,然后中断所有未执行的线程

版权声明:本文为若隐随心原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/itww/p/16722269.html