AccessControlException 并不总是在来自公共 Java 池的线程中抛出

DPM*_*DPM 9 java

我有以下程序:

       public static void main(String[] args) throws Exception{

        ForkJoinTask<?> read = ForkJoinPool.commonPool().submit(new Runnable() {
            @Override
            public void run() {
                SecurityManager appsm = System.getSecurityManager();

                if (appsm != null) {
                    appsm.checkPermission(new PropertyPermission("os.arch", "read"));
                    System.out.println("Permissions are OK!!!!");
                } else {
                    System.out.println("No system mangerrr :(");
                }
            }
        });


        read.get();

        System.out.println("End of the program!");
}
Run Code Online (Sandbox Code Playgroud)

我还设置了这个系统属性: -Djava.security.manager

我的问题如下 - 为什么有时我会收到“程序结束!” 写入标准输出,但有时我得到Exception in thread "main" java.util.concurrent.ExecutionException: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "os.arch" "read")

因为公共池的线程是在没有任何权限的情况下创建的,所以它不应该总是向我抛出这个异常吗?这种行为不应该总是相同的,并且在不同的执行过程中不应该有所不同吗?

我正在使用 Zulu OPEN JDK 8.0.252 从我的 IntelliJ IDE 运行它。

tep*_*pic 7

发生这种情况是因为提交给 的任务ForkJoinPool可能会在 上的调用线程中执行join。在您的情况下,这会在您致电时发生read.get();大多数时候,公共池中的工作人员会拿起任务并执行它(并且安全检查失败),但有时任务在调用线程中运行(并且安全检查通过)。

您可以通过记录线程名称来确认这一点。

public void run() {
    SecurityManager appsm = System.getSecurityManager();
    if (appsm != null) {
        System.err.println("Thread: " + Thread.currentThread().getName());
        appsm.checkPermission(new PropertyPermission("os.arch", "read"));
        System.out.println("Permissions are OK!!!!");
    } else {
        System.out.println("No system mangerrr :(");
    }
}
Run Code Online (Sandbox Code Playgroud)

这将Thread: ForkJoinPool.commonPool-worker-1在安全检查失败和Thread: main安全检查通过时记录。

两种情况之间的行为差​​异是因为您的主线程具有os.arch read权限,但公共池中的线程没有权限。

ForkJoinPooljavadoc:

公共池默认使用默认参数构建,但可以通过设置三个系统属性来控制这些参数:

  • java.util.concurrent.ForkJoinPool.common.parallelism - 并行度,非负整数
  • java.util.concurrent.ForkJoinPool.common.threadFactory - ForkJoinPool.ForkJoinWorkerThreadFactory 的类名
  • java.util.concurrent.ForkJoinPool.common.exceptionHandler - Thread.UncaughtExceptionHandler 的类名

如果存在 SecurityManager 且未指定工厂,则默认池使用工厂提供未启用权限的线程

公共池中的默认安全性非常保守。它在没有特权的情况下运行任务并ThreadLocal在任务完成后清理变量以防止信息泄漏。

公共池的安全性必须是保守的,因为它对所有代码都是可用的——如果它默认在 main 下运行,SecurityManager那么您尝试沙箱化的任何代码都可以通过在公共池中运行任务来提升权限。

如果您不想让代码以SecurityManager您在命令行上配置的权限允许的权限在公共池中运行任务,那么您必须将 设置为java.util.concurrent.ForkJoinPool.common.threadFactory您提供的实现,例如

public final class CustomForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {
    @Override
    public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        return ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
    }
}
Run Code Online (Sandbox Code Playgroud)