详解Java8与Runtime.getRuntime().availableProcessors()
作者:小熊的进阶之路 发布时间:2023-01-04 18:17:06
lambda表达式以及并行流。官方承诺你写出来的代码更运行得更快。流会自动通过Fork/Join池并行地执行。我听过一些关于Java 8的主题的演讲,不过在这个非常关键的点上它们都说的有点问题。我计划在后续的文章中对并行流进行下深入的讲解,在这之前我先花点时间仔细地分析下它。关于这个问题,我只想问你们一个非常简单的问题,不过也是一个非常重要的问题,因为它是很多问题的关键所在。这个问题是:
这些并行操作的线程都是从哪来的?
在Java 8里,我们有一个通用的Fork/Join池,我们可以通过ForkJoinPool.commonPool()来访问它。并行流,并行排序,CompletableFuture等都会用到它。当你构造一个Fork/Join池的时候,通常你都没有指定最大线程数。你只是指定了一个期望的并发数,也就是说你希望在运行时的同一时间有多少活跃的线程。当线程被阻塞在一个phaser的时候,会创建另一个线程来保证池里有足够的活跃线程。这个phaser就是触发这个行为的同步器。Fork/Join池最大的线程数是32767,但在远没达到这个数量时,在大多数操作系统上就会抛出OutOfMemoryError异常了。在这段示例代码中,我会不断创建新的RecursiveAction真到达到第一个阶段(也就是到达了200个线程)。如果我们增加到一个更大的数字,比如说到100000,这段代码就会失败了。
import java.util.concurrent.*;
public class PhaserForkJoin {
public static void main(String... args) {
ForkJoinPool common = ForkJoinPool.commonPool();
Phaser phaser = new Phaser(200);
common.invoke(new PhaserWaiter(phaser));
}
private static class PhaserWaiter extends RecursiveAction {
private final Phaser phaser;
private PhaserWaiter(Phaser phaser) {
this.phaser = phaser;
System.out.println(ForkJoinPool.commonPool().getPoolSize());
}
protected void compute() {
if (phaser.getPhase() > 0) return; // we've passed first phase
PhaserWaiter p1 = new PhaserWaiter(phaser);
p1.fork();
phaser.arriveAndAwaitAdvance();
p1.join();
}
}
}
Fork/Join池没有一个最大线程数,只有一个期望并发数,这是指我们希望同时有多少个活跃线程。
通用池是很有用的,因为它意味着不同类型的作业可以共享同一个池,而不用超出代码所运行的机器上期望并发数。当然了,如果一个线程由于非Phaser的其它原因阻塞了,那可能这个通用池的表现就和预期的不太一样了。
什么是通用FJ池的默认的期望并发数?
通常的FJ池的期望并发数的默认值是Runtime.getRuntime().availableProcessors() -1。如果你在一个双核的机器上通过Arrays.parallelSort()来运行并行排序的话,默认使用的是普通的Arrays.sort()方法。尽管Oracle的官方文档可能许诺你可以获得性能提升,但是你在一个双核的机器上可能完全看不着任何提升。
然而,更大的问题在于Runtime.getRuntime().availableProcessors()也并非都能返回你所期望的数值。比如说,在我的双核1-2-1机器上,它返回的是2,这是对的。不过在我的1-4-2机器 上,也就是一个CPU插槽,4核,每个核2个超线程,这样的话会返回8。不过我其实只有4个核,如果代码的瓶颈是在CPU这块的话,我会有7个线程在同时 竞争CPU周期,而不是更合理的4个线程。如果我的瓶颈是在内存这的话,那这个测试我可以获得7倍的性能提升。
不过这还没完!Java Champions上的一个哥们发现了一种情况,他有一台16-4-2的机器 (也就是16个CPU插槽,每个CPU4个核,每核两个超线程,返回的值居然是16!从我的i7 Macbook pro上的结果来看,我觉得应该返回的是16*4*2=128。在这台机器上运行Java 8的话,它只会将通用的FJ池的并发数设置成15。正如 Brian Goetz所指出的,“虚拟机其实不清楚什么是处理器,它只是去请求操作系统返回一个值。同样的,操作系统也不知道怎么回事,它是去问的硬件设备。硬件会告诉它一个值,通常来说是硬件线程数。操作系统相信硬件说的,而虚拟机又相信操作系统说的。”
所幸的是还有一个解决方案。启动的时候,你可以通过系统属性 java.util.concurrent.ForkJoinPool.common.parallelism来设置通用池的并发数。也就是说,我们可以通过-Djava.util.concurrent.ForkJoinPool.common.parallelism=128来启动这段程序,现在你可以看到它的并发数是128了:
import java.util.concurrent.*;
public class ForkJoinPoolCommon {
public static void main(String... args) {
System.out.println(ForkJoinPool.commonPool());
}
}
还有两个控制通用池的额外的系统属性。如果你希望处理未捕获异常的话,你可以通过java.util.concurrent.ForkJoinPool.common.exceptionHandler来指定一个处理类。如果你希望有自己的线程工厂的话,可以通过 java.util.concurrent.ForkJoinPool.common.threadFactory来配置。默认的Fork/Join池的工厂生成的是守护线程,可能你的应用里面不希望使用它。不过如果你这么做的话请小心——这样你就无法关闭这个通用池了。
来源:https://www.cnblogs.com/xiaoxiong2015/p/12705438.html


猜你喜欢
- 前面有文章曾经地介绍过MediaPlayer的基本用法,这里就更加深入地讲解MediaPlayer的在线播放功能。本文主要实现MediaPl
- 排序二叉树概念二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。是数据
- 一般表单数据分为两类<form method="post" action="${pageContext.
- IDEA自动跳出括号并且补全分号(类似eclipse的功能)跳括号外头去ctrl shift enter叫做 Complete Curren
- 首先说一下,教科书上的扫描线算法确实是用c++很好实现,而且网上有很多源码,而java实现的基本没有(可能是我没看到),所以肖先生还是打算自
- 1、图像灰度化:public Bitmap bitmap2Gray(Bitmap bmSrc) { // 得到图片的长和宽
- json格式的字符串与对象的互相转换Jackson 简介Jackson是一个简单基于Java应用库,Jackson可以轻松的将Java对象转
- 前言传统的Restful API 存在诸多的问题,首先它无法控制返回的字段,前端也无法预判后端的返回结果,另外不同的返回结果对应不同的请求地
- 为什么要使用克隆? 想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了,Java语言中克隆针对的是类的实例。如何实现对象
- javax.persistence 介绍Spring Data JPA 采用约定大于配置的思想,默认了很多东西JPA是存储业务实体关联的实体
- io学习框架:文件:保存数据的地方。1)常见文件对象的相关构造器和方法:当进行File file = new File(filePath);
- 在C#中DateTime是一个包含日期、时间的类型,此类型通过ToString()转换为字符串时,可根据传入给Tostring()的参数转换
- 以前只知道控件的onTouchEvent()事件,它的动作有MotionEvent.ACTION_DOWN、MotionEvent.ACTI
- 代理模式代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采
- 在项目开发中某些场景必须要用到启动项目后立即执行方式的功能,如我们需要去初始化数据到redis缓存,或者启动后读取相应的字典配置等,这篇文章
- 本文实例讲述了Android基于ImageView绘制的开关按钮效果。分享给大家供大家参考,具体如下: 今天弄了一下用图片绘制开关
- 比如在类上使用该注解 @Alias("dDebtEntity")则在mapper.xml文件中resultType=&q
- Random random = new Random((int)(DateTime.Now.Ticks)); &nbs
- 话不多说,请看代码:<!DOCTYPE html><html><head> <meta
- 转发和重定向相同点都是web开发中资源跳转的方式。不同点转发:是服务器内部的跳转,浏览器的地址栏不会发生变化。从一个页面到另一个页面的跳转还