MST

星途 面试题库

面试题:Java Lambda表达式在多线程环境下的资源共享与同步问题

假设有多个线程通过Lambda表达式创建,它们需要共享一个资源。请编写代码实现这一场景,并使用合适的同步机制来确保资源的线程安全。解释为什么选择这种同步机制,以及Lambda表达式在这种多线程资源共享场景中的潜在风险与应对策略。
11.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

代码实现

以下是使用Java语言实现多线程通过Lambda表达式创建并共享资源的代码示例,使用synchronized关键字确保资源线程安全:

class SharedResource {
    private int value;

    public SharedResource(int initialValue) {
        this.value = initialValue;
    }

    public synchronized void increment() {
        value++;
    }

    public synchronized int getValue() {
        return value;
    }
}

public class ThreadWithLambda {
    public static void main(String[] args) {
        SharedResource sharedResource = new SharedResource(0);

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                sharedResource.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                sharedResource.increment();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final value: " + sharedResource.getValue());
    }
}

选择同步机制的原因

选择synchronized关键字是因为它是Java中最基本的同步机制。它可以修饰方法或代码块,确保在同一时间只有一个线程能够访问被同步的代码,从而保证共享资源的线程安全。对于简单的多线程资源共享场景,synchronized使用方便,并且能够满足基本的线程安全需求。

Lambda表达式在多线程资源共享场景中的潜在风险与应对策略

  1. 潜在风险
    • 数据竞争:Lambda表达式本身并不会自动处理线程同步问题。如果多个线程在Lambda表达式中同时访问和修改共享资源,就会发生数据竞争,导致结果不可预测。例如在上述代码中,如果没有synchronized关键字,两个线程同时调用increment方法时,就可能出现值覆盖的情况,导致最终结果不是预期的2000。
    • 闭包问题:Lambda表达式会捕获外部变量。如果在多线程环境下,被捕获的变量状态不一致,也会引发问题。例如捕获了一个可变对象,不同线程对该对象的修改可能导致程序逻辑错误。
  2. 应对策略
    • 使用同步机制:如上述代码中使用synchronized关键字确保对共享资源的访问是线程安全的。也可以使用java.util.concurrent包中的锁机制,如ReentrantLock,它提供了更灵活的锁控制。
    • 不可变对象:尽量使用不可变对象。如果Lambda表达式捕获的是不可变对象,就不用担心对象状态被修改带来的问题。例如使用String而不是StringBuilder,因为String是不可变的。
    • 线程局部变量:使用ThreadLocal类。它为每个线程提供了独立的变量副本,避免了线程间的数据竞争。例如,如果共享资源在线程间不需要共享状态,可以使用ThreadLocal来存储每个线程自己的资源副本。