面试题答案
一键面试设计思路
- 状态共享:使用
AtomicInteger
等原子类来保存需要共享的状态信息,因为原子类提供了线程安全的操作,避免在多线程环境下的数据竞争。 - 避免副作用:将状态更新操作封装在
map
、filter
等中间操作的函数式接口实现中,确保每个操作都是纯函数,即相同的输入始终产生相同的输出,且不产生可观察的副作用。 - 线程安全:在多线程场景下,使用原子类以及并行流时注意流操作本身的线程安全性。并行流会自动利用多核处理器并行处理数据,原子类可以保证共享状态的线程安全更新。
具体实现方式
假设我们有一个List<String>
,要统计其中特定字符串(例如"target")出现的次数。
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class StreamStateSharing {
public static void main(String[] args) {
List<String> list = Arrays.asList("target", "other", "target", "yetAnother");
AtomicInteger count = new AtomicInteger(0);
list.stream()
.map(s -> {
if ("target".equals(s)) {
count.incrementAndGet();
}
return s;
})
.filter(s -> !"target".equals(s))
.forEach(System.out::println);
System.out.println("Target count: " + count.get());
}
}
在上述代码中:
- 我们创建了一个
AtomicInteger
实例count
来统计特定字符串出现的次数。 - 在
map
操作中,当遇到特定字符串时,使用AtomicInteger
的incrementAndGet
方法安全地增加计数。 - 后续的
filter
操作和forEach
操作不受状态更新的影响,保证了流操作的正确性和可重复性。
多线程场景下保证线程安全
如果使用并行流,代码如下:
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class ParallelStreamStateSharing {
public static void main(String[] args) {
List<String> list = Arrays.asList("target", "other", "target", "yetAnother");
AtomicInteger count = new AtomicInteger(0);
list.parallelStream()
.map(s -> {
if ("target".equals(s)) {
count.incrementAndGet();
}
return s;
})
.filter(s -> !"target".equals(s))
.forEach(System.out::println);
System.out.println("Target count: " + count.get());
}
}
并行流会自动并行处理数据,而AtomicInteger
的原子操作保证了count
在多线程环境下的安全更新。每个线程都可以独立地更新count
,不会产生数据竞争问题。