SecurityException from I/O code in a parallel stream(来自并行流中的 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);
}
});
}
}
在启用安全管理器的情况下运行时,仅在流上调用 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/Join 公共池.这是一个实现细节,但正如在这种情况下观察到的那样,这些细节可能会以意想不到的方式泄露出去.
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 公共池的线程工厂被设置为创建无害线程的工厂.这样的 innocuous 线程没有授予它任何权限,不是任何已定义线程组的成员,并且在顶级 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:来自并行流中的 I/O 代码的 SecurityException
基础教程推荐
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01
- 如何对 HashSet 进行排序? 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01