java - 是否可以仅同步 java 流中的终端方法调用?

标签 java multithreading java-stream

让我们考虑可以从不同线程访问的集合。我正在寻找一种线程安全的方法来使用 java 流操作集合。

由于所有中间流操作都是惰性的,所以真正的作业只能在终端方法中执行。我只能同步终端方法调用。

那么,代码是否线程安全:

public void print(Collection<String> list){
      Stream stream = list.stream();
      synchronized(this){
          stream.forEach(p -> System.out.println(p)); 
      }
}

最佳答案

当然,这取决于您的应用程序 synchronized(this) 是否足以(或根本必要)来阻止在 Stream 遍历期间对源 Collection 进行操作。

关于惰性,确切的行为取决于源集合。考虑the contract of Collection.spliterator() :

In order to preserve expected laziness behavior for the stream() and parallelStream() methods, spliterators should either have the characteristic of IMMUTABLE or CONCURRENT, or be late-binding. If none of these is practical, the overriding class should describe the spliterator's documented policy of binding and structural interference, and should override the stream() and parallelStream() methods to create streams using a Supplier of the spliterator, as in:

Stream<E> s = StreamSupport.stream(() -> spliterator(), spliteratorCharacteristics)

These requirements ensure that streams produced by the stream() and parallelStream() methods will reflect the contents of the collection as of initiation of the terminal stream operation.

为了缩短这一点,上面列出的所有变体都意味着允许在开始终端操作之前对源 Collection 进行修改(当然,不包括 IMMUTABLE spliterators),并将由 Stream 操作反射(reflect).

因此,如果您的应用程序保护对源列表的所有操作,以防止在执行终端操作时对源列表进行操作,那么您就安全了。但请注意,接收列表作为参数,但在 this 上进行同步的方法看起来并没有正确保护列表。只是为了明确一点:所有线程必须在同一对象上同步以保护特定的共享资源。

但抛开这一点,假设操作 list 的所有线程都将在 listLock 上正确同步,则以下代码将起作用:

Stream stream = list.stream()
    .intermediateOp1(…)
    .intermediateOp2(…);

synchronized(listLock){
    stream.forEach(p -> System.out.println(p)); 
}

但这毫无意义。正如您自己所说,这样做的原因是,将中间操作链接到流不会导致任何实际处理。这是一个非常便宜的东西,因此将其从 protected 代码部分中排除只意味着持有锁的时间可能会相差几纳秒。

你几乎不会注意到有什么不同

synchronized(listLock) {
    list.stream()
        .intermediateOp1(…)
        .intermediateOp2(…)
        .forEach(p -> System.out.println(p)); 
}

因为无论如何,大部分时间都花在 forEach 中。

关于java - 是否可以仅同步 java 流中的终端方法调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40505243/

相关文章:

java - amazon EC2 tomcat启动貌似成功,但是无法访问8080端口页面

java 扫描仪在 .next() 不应该阻塞时阻塞

c# - 如何锁定具有相同ID的对象?

Java 8 迭代列表内的列表和stream.map()

java - 在 map 集合中添加多个值的有效方法

java - 线程 "main"java.lang.ClassNotFoundException : WordCount 中的异常

java - 使用 netbeans 获取 Assets android 错误,修复?

java - 创建每 XXXXms 调用一个方法的计时器

swift - 线程 1 exc_bad_instruction.. 快速错误

java - 如何使用 Lambda 在 java1.8 中的字符串集合中搜索特定项?