SecurityException from I/O code in a parallel stream

前端 未结 2 1606
清酒与你
清酒与你 2020-12-31 11:17

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;
imp         


        
2条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-31 11:55

    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.

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

    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.

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

    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.

    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

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

    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.

提交回复
热议问题