使用Java8流API,是否有可能强制从消费者那里顺序处理Stream?
如果我有一个java.util.function.Consumer
,我知道它不是线程安全的,我想强制Streams API按顺序处理它,因为并行运行会始终'导致越野行为。
此代码段说明了我的问题:
static class NonThreadsafeConsumer<T> implements Consumer<T> {
@Override
public void accept(T arg0) {
//Do non-threadsafe stuff here
}
}
public void doIt(Stream<String> stream) {
//Unknown behaviour
stream.forEach(new NonThreadsafeConsumer<>());
// Bug for sure
stream.parallel().forEach(new NonThreadsafeConsumer<>());
// Correct
stream.sequential().forEach(new NonThreadsafeConsumer<>());
}
我遇到的问题是{作为NonThreadsafeConsumer
的作者,我不想相信doIt()
方法的实现者始终知道并记住放入.sequential()
调用
(注意使消费者线程安全不是我的问题,我只想知道是否可以这样做)
答案 0 :(得分:3)
没有办法强制执行Consumer
的单线程使用,至少不会没有相同数量级的开销,而不仅仅是使Consumer
线程安全。
但Consumer
强制执行单线程使用不是责任。在Java中,不是线程安全是可变类的标准,比如StringBuilder
,ProcessBuilder
,ArrayList
,HashMap
,任何类型的迭代器,DecimalFormat
,列举一些广泛使用的可变类的示例,这些类不是线程安全的,也不强制执行单线程使用。
请注意,您只需向使用者的synchronized
方法添加accept
即可一次强制执行单个线程。在顺序上下文中使用时,JVM的优化器有可能消除相关的开销。
但最简单的解决方案是记录要求并完成。如果有人错误地使用了您的课程,他们将会遇到他们要求的问题。您可以尽最大努力执行有效性检查,但尝试使软件防弹可能会浪费很多努力而无法获益。
答案 1 :(得分:0)
您可以引入工厂模式,强制用户从工厂获取Consumer,而不是直接实例化它。
工厂可以使用布尔参数来指示流是否并行。
这不是最好的工作,但它可能有助于doIt()
的实施者不会错过使流顺序。
希望这有助于或至少为您提供有关如何实施此功能的一些想法。
public static void main(String[] args) {
List<String> strings = Arrays.asList("a", "b", "b");
strings.stream()
.forEach(ConsumerFactory.getConsumer(false));
}
static class ConsumerFactory {
static Consumer getConsumer(boolean isPar) {
if (isPar) {
//Handle Parallel
}
return new NonThreadsafeConsumer<>();
}
}
static class NonThreadsafeConsumer<T> implements Consumer<T> {
@Override
public void accept(T t) {
//Do non-threadsafe stuff here
}
}