来自并行流中的I / O代码的SecurityException [英] SecurityException from I/O code in a parallel stream

查看:171
本文介绍了来自并行流中的I / O代码的SecurityException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法解释这个问题,但我在其他人的代码中发现了这种现象:

I have no way to explain this one, but I found this phenomenon in somebody else's code:

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.util.stream.Stream;

import org.junit.Test;

public class TestDidWeBreakJavaAgain
{
    @Test
    public void testIoInSerialStream()
    {
        doTest(false);
    }

    @Test
    public void testIoInParallelStream()
    {
        doTest(true);
    }

    private void doTest(boolean parallel)
    {
        Stream<String> stream = Stream.of("1", "2", "3");
        if (parallel)
        {
            stream = stream.parallel();
        }
        stream.forEach(name -> {
            try
            {
                Files.createTempFile(name, ".dat");
            }
            catch (IOException e)
            {
                throw new UncheckedIOException("Failed to create temp file", e);
            }
        });
    }
}

在启用安全管理器的情况下运行时,只需调用<流上的code> parallel(),或者从集合中获取流时的 parallelStream(),似乎保证所有尝试执行I / O将抛出 SecurityException 。 (最有可能的是,调用可以抛出 SecurityException 的任何方法,抛出。)

When run with the security manager enabled, merely calling parallel() on a stream, or parallelStream() when getting the stream from a collection, seems to guarantee that all attempts to perform I/O will throw SecurityException. (Most likely, calling any method which can throw SecurityException, will throw.)

我理解 parallel()意味着它将在另一个可能与我们开始时没有相同权限的线程中运行,但我想我认为框架会照顾我们。

I understand that parallel() means that it will be running in another thread which might not have the same privileges as the one we started with, but I guess I thought that the framework would take care of that for us.

删除对 parallel()的调用整个代码库中的 parallelStream()可以避免风险。插入 AccessController.doPrivileged 也修复了它,但对我来说听起来并不安全,至少在所有情况下都没有。还有其他选择吗?

Removing calls to parallel() or parallelStream() throughout the codebase avoids the risk. Inserting an AccessController.doPrivileged also fixes it, but doesn't sound safe to me, at least not in all situations. Is there any other option?

推荐答案

并行流执行将使用Fork / Join框架,更具体地说它将使用Fork /加入公共游泳池。这是一个实现细节,但在这种情况下观察到这些细节可能会以意想不到的方式泄漏。

Parallel stream execution will use the Fork/Join framework, more specifically it will use the Fork/Join common pool. This is an implementation detail, but as observed in this case such details can leak out in unexpected ways.

请注意,使用异步执行任务时也会发生相同的行为 CompletableFuture

Note that the same behaviour can also occur when executing a task asynchronously using CompletableFuture.

当安全管理器出现时,Fork / Join公共池的线程工厂设置为a创建无害线程的工厂。这样的无害的线程没有授予它的权限,不是任何已定义的线程组的成员,并且在顶级Fork / Join任务完成其执行后,所有线程本地(如果创建)都是清除。这种行为确保在共享公共池时,Fork / Join任务彼此隔离。

When a security manager is present the thread factory of the Fork/Join common pool is set to a factory that creates innocuous threads. Such an innocuous thread has no permissions granted to it, is not a member of any defined thread group, and after a top-level Fork/Join task has completed its execution all thread locals (if created) are cleared. Such behaviour ensures Fork/Join tasks are isolated from each other when sharing the common pool.

这就是为什么在示例中 SecurityException 被抛出,可能是:

This is why in the example a SecurityException is thrown, probably:


java.lang.SecurityException:无法创建临时文件或目录

java.lang.SecurityException: Unable to create temporary file or directory

有两种潜在的解决方法。根据安全经理使用的原因,每次解决可能会增加不安全的风险。

There are two potential work arounds. Depending on the reasons a security manager utilized, each work around may increase the risk of being insecure.

第一个更一般的解决方法是注册一个Fork / Join线程工厂通过系统属性告诉Fork / Join框架公共池的默认线程工厂应该是什么。例如,这是一个非常简单的线程工厂:

The first, more general, work around is to register a Fork/Join thread factory via a system property to tell the Fork/Join framework what the default thread factory should be for the common pool. For example here is a really simple thread factory:

public class MyForkJoinWorkerThreadFactory
        implements ForkJoinPool.ForkJoinWorkerThreadFactory {
    public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        return new ForkJoinWorkerThread(pool) {};
    }
}

可以使用以下系统属性注册:

Which can be registered with the following system property:


-Djava.util.concurrent.ForkJoinPool.common.threadFactory = MyForkJoinWorkerThreadFactory

-Djava.util.concurrent.ForkJoinPool.common.threadFactory=MyForkJoinWorkerThreadFactory

MyForkJoinWorkerThreadFactory 的行为目前相当于
ForkJoinPool.defaultForkJoinWorkerThreadFactory

The behaviour of MyForkJoinWorkerThreadFactory is currently equivalent to that of ForkJoinPool.defaultForkJoinWorkerThreadFactory.

第二个更具体的解决方法是创建一个新的Fork / Join池。在这种情况下, ForkJoinPool.defaultForkJoinWorkerThreadFactory 将用于不接受 ForkJoinWorkerThreadFactory 参数的构造函数。任何并行流执行都需要在从该池内执行的任务中执行。请注意,这是一个实现细节,在将来的版本中可能会或可能不会。

The second, more specific, work around is to create a new Fork/Join pool. In this case the ForkJoinPool.defaultForkJoinWorkerThreadFactory will be utilized for constructors not accepting a ForkJoinWorkerThreadFactory argument. Any parallel stream execution would need to be performed from within a task executed from within that pool. Note that this is an implementation detail and may or may not work in future releases.

这篇关于来自并行流中的I / O代码的SecurityException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆