对Java中的非线程安全使用者强制执行顺序评估

时间:2018-04-24 15:11:13

标签: java thread-safety java-stream

使用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()调用

(注意使消费者线程安全不是我的问题,我只想知道是否可以这样做)

2 个答案:

答案 0 :(得分:3)

没有办法强制执行Consumer的单线程使用,至少不会没有相同数量级的开销,而不仅仅是使Consumer线程安全。

Consumer强制执行单线程使用不是责任。在Java中,不是线程安全是可变类的标准,比如StringBuilderProcessBuilderArrayListHashMap,任何类型的迭代器,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
    }
}