面试题答案
一键面试Java Lambda表达式在JVM中的底层实现机制
- 编译:
- Lambda表达式在编译时,会被转换为一个匿名类或一个方法引用。如果Lambda表达式没有捕获外部变量,它通常会被编译成一个静态方法。例如,
(int a, int b) -> a + b
这种简单的无状态Lambda表达式,会被编译成一个静态方法。 - 如果Lambda表达式捕获了外部变量(局部变量需为effectively final),它会被编译成一个匿名内部类,该类持有对外部变量的引用。例如:
- Lambda表达式在编译时,会被转换为一个匿名类或一个方法引用。如果Lambda表达式没有捕获外部变量,它通常会被编译成一个静态方法。例如,
int num = 10;
Runnable r = () -> System.out.println(num);
这里的Lambda表达式 () -> System.out.println(num)
会被编译成一个匿名内部类,该类持有对 num
的引用。
2. 存储:
- 编译后的匿名类或方法引用会像普通的类和方法一样存储在JVM的方法区。对于匿名类,它会有自己的类结构信息,包括类名(通常是包含数字的匿名类名)、字段(如果捕获了外部变量)和方法。方法引用会在常量池中存储对目标方法的引用。
- 调用:
- 当调用Lambda表达式时,如果是编译成匿名类,会创建匿名类的实例,然后调用其重写的方法。例如上面的
Runnable
的Lambda表达式,会创建匿名类实例,然后调用其run
方法。 - 如果是编译成静态方法,会直接调用该静态方法。例如对于简单的无状态Lambda表达式
(int a, int b) -> a + b
,会直接调用其编译后的静态方法。
- 当调用Lambda表达式时,如果是编译成匿名类,会创建匿名类的实例,然后调用其重写的方法。例如上面的
高并发场景下优化Lambda表达式使用的思路及示例代码
- 优化思路:
- 减少捕获外部变量:捕获外部变量会增加对象的创建和内存开销,尽量使用无状态的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);
}
}
- 示例代码:
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表达式是无状态的,避免了捕获外部变量带来的性能开销。同时,使用线程池来管理多线程任务,提高了线程的复用性和整体性能。