MST

星途 面试题库

面试题:Java Lambda表达式的底层原理及优化

阐述Java Lambda表达式在JVM中的底层实现机制,包括如何编译、如何存储和调用。并说明在高并发场景下,如何优化Lambda表达式的使用以提高性能,给出具体的优化思路和示例代码。
41.8万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

Java Lambda表达式在JVM中的底层实现机制

  1. 编译
    • Lambda表达式在编译时,会被转换为一个匿名类或一个方法引用。如果Lambda表达式没有捕获外部变量,它通常会被编译成一个静态方法。例如,(int a, int b) -> a + b 这种简单的无状态Lambda表达式,会被编译成一个静态方法。
    • 如果Lambda表达式捕获了外部变量(局部变量需为effectively final),它会被编译成一个匿名内部类,该类持有对外部变量的引用。例如:
int num = 10;
Runnable r = () -> System.out.println(num);

这里的Lambda表达式 () -> System.out.println(num) 会被编译成一个匿名内部类,该类持有对 num 的引用。 2. 存储

  • 编译后的匿名类或方法引用会像普通的类和方法一样存储在JVM的方法区。对于匿名类,它会有自己的类结构信息,包括类名(通常是包含数字的匿名类名)、字段(如果捕获了外部变量)和方法。方法引用会在常量池中存储对目标方法的引用。
  1. 调用
    • 当调用Lambda表达式时,如果是编译成匿名类,会创建匿名类的实例,然后调用其重写的方法。例如上面的 Runnable 的Lambda表达式,会创建匿名类实例,然后调用其 run 方法。
    • 如果是编译成静态方法,会直接调用该静态方法。例如对于简单的无状态Lambda表达式 (int a, int b) -> a + b,会直接调用其编译后的静态方法。

高并发场景下优化Lambda表达式使用的思路及示例代码

  1. 优化思路
    • 减少捕获外部变量:捕获外部变量会增加对象的创建和内存开销,尽量使用无状态的Lambda表达式。例如,避免在Lambda表达式中引用外部的可变局部变量。
    • 使用并行流:在处理集合等数据结构时,使用并行流可以充分利用多核CPU的优势。例如,对一个整数列表进行求和操作:
import java.util.Arrays;
import java.util.List;

public class LambdaPerformanceOptimization {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 串行流
        long sequentialSum = numbers.stream().mapToInt(Integer::intValue).sum();
        // 并行流
        long parallelSum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
        System.out.println("Sequential Sum: " + sequentialSum);
        System.out.println("Parallel Sum: " + parallelSum);
    }
}
  • 避免在Lambda中进行同步操作:尽量将同步操作移到Lambda表达式外部,以减少锁争用。例如,如果需要对共享资源进行操作,可以先获取锁,然后在Lambda外部修改共享资源。
  • 使用静态方法引用:相比于匿名类的Lambda表达式,静态方法引用的性能更好,因为它不需要创建额外的对象实例。例如:
import java.util.function.IntBinaryOperator;

public class StaticMethodReference {
    public static int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        IntBinaryOperator operator = StaticMethodReference::add;
        int result = operator.applyAsInt(3, 5);
        System.out.println(result);
    }
}
  1. 示例代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class LambdaConcurrencyOptimization {
    private static final int THREADS = 10;
    private static final ExecutorService executorService = Executors.newFixedThreadPool(THREADS);

    public static void main(String[] args) {
        for (int i = 0; i < THREADS; i++) {
            // 使用无状态的Lambda表达式
            executorService.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " is running.");
            });
        }
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
                if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("Pool did not terminate");
                }
            }
        } catch (InterruptedException ie) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

在上述代码中,submit 方法中的Lambda表达式是无状态的,避免了捕获外部变量带来的性能开销。同时,使用线程池来管理多线程任务,提高了线程的复用性和整体性能。