From df14ba0609219efbee6031c1590ce1236c1f5b59 Mon Sep 17 00:00:00 2001 From: luohaiyang Date: Sun, 24 Apr 2022 23:26:11 +0800 Subject: [PATCH 1/3] =?UTF-8?q?[A]=E4=BD=8D=E8=BF=90=E7=AE=97=E5=88=86?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../concurent/ThreadPoolExecutorDemo.java | 48 ++++++++++ ...20\347\256\227\345\210\206\346\236\220.md" | 95 +++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java create mode 100644 "note/JDK/\345\274\200\346\272\220\351\241\271\347\233\256\351\207\214\351\202\243\344\272\233\347\234\213\344\270\215\346\207\202\347\232\204\344\275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java new file mode 100644 index 0000000..5d5b5fc --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java @@ -0,0 +1,48 @@ +package com.learnjava.concurent; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @Description + * @Author luohaiyang + * @Date 2022/4/24 + */ +public class ThreadPoolExecutorDemo { + + public static void main(String[] args) { + testThreadPoolExecutorBinaryCalc(); + } + + + /** + * 验证ThreadPoolExecutor中的二进制位运算操作 + */ + private static void testThreadPoolExecutorBinaryCalc() { +// System.out.println(ctl.get()); +// System.out.println(Integer.toBinaryString(ctlOf(RUNNING, 0))); +// System.out.println(Integer.toBinaryString(RUNNING)); + // 修改线程状态-STOP + System.out.println(Integer.toBinaryString(~runStateOf(ctlOf(STOP, 10)))); + // 修改线程状态-TERMINATED +// System.out.println(runStateOf(3)); +// System.out.println(Integer.toBinaryString(~CAPACITY)); + } + + private static final int COUNT_BITS = Integer.SIZE - 3; + + private static final int CAPACITY = (1 << COUNT_BITS) - 1; + + private static final int RUNNING = -1 << COUNT_BITS; + private static final int SHUTDOWN = 0 << COUNT_BITS; + private static final int STOP = 1 << COUNT_BITS; + private static final int TIDYING = 2 << COUNT_BITS; + private static final int TERMINATED = 3 << COUNT_BITS; + + private static AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); + + private static int runStateOf(int c) { return c & ~CAPACITY; } + + private static int workerCountOf(int c) { return c & CAPACITY; } + + private static int ctlOf(int rs, int wc) { return rs | wc; } +} diff --git "a/note/JDK/\345\274\200\346\272\220\351\241\271\347\233\256\351\207\214\351\202\243\344\272\233\347\234\213\344\270\215\346\207\202\347\232\204\344\275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" "b/note/JDK/\345\274\200\346\272\220\351\241\271\347\233\256\351\207\214\351\202\243\344\272\233\347\234\213\344\270\215\346\207\202\347\232\204\344\275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" new file mode 100644 index 0000000..2b0bcab --- /dev/null +++ "b/note/JDK/\345\274\200\346\272\220\351\241\271\347\233\256\351\207\214\351\202\243\344\272\233\347\234\213\344\270\215\346\207\202\347\232\204\344\275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" @@ -0,0 +1,95 @@ +相信看过几个流行框架源码的小伙伴,或多或少都见到过底层代码运用的位运算,不知道有多少是能够一眼看懂了的,一眼看懂了的都是“真大佬”。如果看不懂的话就老老实实的通过二进制分析来看下这些二进制算法的作用。 + +## 1. JDK1.8 HashMap里运用到的为运算 + +## 2. Netty里运用的位运算 + +## 3. JDK ThreadPoolExecutor里的位运算 + +```java +public class ThreadPoolExecutor extends AbstractExecutorService { + + // ... 其他代码省略 + + private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); + private static final int COUNT_BITS = Integer.SIZE - 3; + private static final int CAPACITY = (1 << COUNT_BITS) - 1; + + private static final int RUNNING = -1 << COUNT_BITS; + private static final int SHUTDOWN = 0 << COUNT_BITS; + private static final int STOP = 1 << COUNT_BITS; + private static final int TIDYING = 2 << COUNT_BITS; + private static final int TERMINATED = 3 << COUNT_BITS; + + private static int runStateOf(int c) { return c & ~CAPACITY; } + private static int workerCountOf(int c) { return c & CAPACITY; } + private static int ctlOf(int rs, int wc) { return rs | wc; } + + private static boolean runStateLessThan(int c, int s) { + return c < s; + } + + private static boolean runStateAtLeast(int c, int s) { + return c >= s; + } + + private static boolean isRunning(int c) { + return c < SHUTDOWN; + } + + // ... 其他代码省略 +} +``` +首先看下ctlOf()方法,入参是int rs和int wc,这里rs其实是线程池里线程的状态,而wc表示的时线程数,基于这两个点我们进行位运算分析。 + +首先看先成变量: +```java +private static final int COUNT_BITS = Integer.SIZE - 3; +private static final int RUNNING = -1 << COUNT_BITS; +``` +Integer.SIZE = 32,所以COUNT_BITS = 29,这里RUNNING就是-1的二进制位左移29位,得到的结果就是(提示:-1的二进制是: 1111 1111 1111 1111 ... 三十二位全是1) +``` +1110 0000 0000 0000 0000 0000 0000 0000 +``` +这就是RUNNING的二进制值。 +同理我们可以分别得到SHUTDOWN、STOP、TIDYING、TERMINATED的二进制值 +``` +0000 0000 0000 0000 0000 0000 0000 0000 // SHUTDOWN +0010 0000 0000 0000 0000 0000 0000 0000 // STOP +0100 0000 0000 0000 0000 0000 0000 0000 // TIDYING +0110 0000 0000 0000 0000 0000 0000 0000 // TERMINATED +``` +这里其实已经可以看出作者的用意了,就是让高3位作为线程池的状态,低29位用来表示线程数量。对于 +```java +private static int ctlOf(int rs, int wc) { return rs | wc; } +// 位运算“或”,遇1得1,否则为0 +``` +所以ctlOf就表示将rs代表的线程状态和wc代表的线程数计算在同一个32位二进制中,互相不影响。 +所以如下: +```java +private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); +// 1110 0000 0000 0000 0000 0000 0000 0000 +``` +接着,再来分析下另外两个方法:runStateOf()、workerCountOf(),这两个方法都喝CAPACITY有关,先看下CAPACITY属性 +```java +private static final int CAPACITY = (1 << COUNT_BITS) - 1; +// 1 << 29 => 0010 0000 0000 0000 0000 0000 0000 0000 +// 1 << 29 - 1 => 0001 1111 1111 1111 1111 1111 1111 1111 + + +private static int runStateOf(int c) { return c & ~CAPACITY; } +// ~CAPACITY => 1110 0000 0000 0000 0000 0000 0000 0000 +// 运算“与”表示11得1,否则为0,所以 c & ~CAPACITY实际上就只能操作高三位, +// 也就是只能计算线程状态,并且~CAPACITY表示的是RUNNING时的状态 + + +private static int workerCountOf(int c) { return c & CAPACITY; } +// CAPACITY => 0001 1111 1111 1111 1111 1111 1111 1111 +// 所以 c & CAPACITY 就表示只能操作低29位,所以workerCountOf就只能操作线程数 +``` +这里需要注意的是,runStateOf()和workerCountOf()传入的数字都是需要由:ctlOf()计算返回的,否则计算会出错。 + +线程池位运算相关验证代码于: + + +未完待续... \ No newline at end of file From 97604e90be60878f67e4001b4365fc89ea1cf017 Mon Sep 17 00:00:00 2001 From: luohaiyang Date: Sun, 24 Apr 2022 23:29:58 +0800 Subject: [PATCH 2/3] =?UTF-8?q?[A]=20=E6=B7=BB=E5=8A=A0=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + ...275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fee20a8..fae4153 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ SpringCloud源码 - [深入学习Java volatile关键字](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/JDK/%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0Java%20volatile%E5%85%B3%E9%94%AE%E5%AD%97.md) - [深入学习Thread底层原理](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/JDK/%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0Thread%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81.md) - [深入学习JDK1.7、8 HashMap扩容原理]() + - [开源项目里那些看不懂的位运算分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/JDK/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E9%87%8C%E9%82%A3%E4%BA%9B%E7%9C%8B%E4%B8%8D%E6%87%82%E7%9A%84%E4%BD%8D%E8%BF%90%E7%AE%97%E5%88%86%E6%9E%90.md) - Spring源码学习 - Spring版本:5.2.1.RELEASE diff --git "a/note/JDK/\345\274\200\346\272\220\351\241\271\347\233\256\351\207\214\351\202\243\344\272\233\347\234\213\344\270\215\346\207\202\347\232\204\344\275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" "b/note/JDK/\345\274\200\346\272\220\351\241\271\347\233\256\351\207\214\351\202\243\344\272\233\347\234\213\344\270\215\346\207\202\347\232\204\344\275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" index 2b0bcab..cec5185 100644 --- "a/note/JDK/\345\274\200\346\272\220\351\241\271\347\233\256\351\207\214\351\202\243\344\272\233\347\234\213\344\270\215\346\207\202\347\232\204\344\275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" +++ "b/note/JDK/\345\274\200\346\272\220\351\241\271\347\233\256\351\207\214\351\202\243\344\272\233\347\234\213\344\270\215\346\207\202\347\232\204\344\275\215\350\277\220\347\256\227\345\210\206\346\236\220.md" @@ -89,7 +89,8 @@ private static int workerCountOf(int c) { return c & CAPACITY; } ``` 这里需要注意的是,runStateOf()和workerCountOf()传入的数字都是需要由:ctlOf()计算返回的,否则计算会出错。 -线程池位运算相关验证代码于: +线程池位运算相关验证代码于,读者可自行测试以加强理解。 +[ThreadPoolExecutorDemo](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java) 未完待续... \ No newline at end of file From a892201439d38587dea8b65266324c4cd2b394d3 Mon Sep 17 00:00:00 2001 From: CoderBruis <37364336+coderbruis@users.noreply.github.com> Date: Tue, 31 May 2022 18:26:51 +0800 Subject: [PATCH 3/3] =?UTF-8?q?Fix=20=E9=94=99=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix 错字 --- ...\210\266\357\274\210\344\270\213\357\274\211.md" | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md" index d99cbec..8bd2172 100644 --- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md" +++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md" @@ -51,8 +51,9 @@ SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultEx } return loader; } -```Java -getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经缓存了该类型的扩展点加载器,如果没有则new一个该类型的ExtensionLoader并添加进EXTENSION_LOADERS中。但需要注意的是ExtensionLoader的构造方法 +``` + +getExtensionLoader方法首先会去判断EXTENSION_LOADERS缓存中是否已经缓存了该类型的扩展点加载器,如果没有则new一个该类型的ExtensionLoader并添加进EXTENSION_LOADERS中。但需要注意的是ExtensionLoader的构造方法 中,是会先创建默认的ExtensionFactory类型的ExtensionLoader对象,然后调用getAdaptiveExtension()方法创建适配类型的扩展点实现类。 ```Java @@ -112,7 +113,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经 loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); } - // 这里只会返回非Adaptive和非Wrapper类型的扩展点实现类Class,因为Adaptive会被缓存到cachedAdaptiveClasses缓存中,儿Wrapper类型的类会被缓存到cachedWrapperClasses缓存中。 + // 这里只会返回非Adaptive和非Wrapper类型的扩展点实现类Class,因为Adaptive会被缓存到cachedAdaptiveClasses缓存中,而Wrapper类型的类会被缓存到cachedWrapperClasses缓存中。 return extensionClasses; } @@ -267,10 +268,10 @@ public class AdaptiveExtensionFactory implements ExtensionFactory { } ``` -① 中逻辑是这样的,调用ExtensionLoader#getSupportedExtensions()回去加载ExtensionFactory所有的扩展点实现类,并返回一个扩展点名称作为Key,扩展点实现类Class对象为Value的Map集合, +① 中逻辑是这样的,调用ExtensionLoader#getSupportedExtensions()会去加载ExtensionFactory所有的扩展点实现类,并返回一个扩展点名称作为Key,扩展点实现类Class对象为Value的Map集合, 在上面的SPI配置文件中已经展示出来了,所以这里获取到的是spi。 -// 有人可能会问,上面的SPI配置文件不是还有一个adaptive吗?为什么没加载进来呢?这是因为getSupportedExtension()中实际是调用getExtensionClasses()方法去获取Map集合,而其底层是去从cachedClasses缓存中 +有人可能会问,上面的SPI配置文件不是还有一个adaptive吗?为什么没加载进来呢?这是因为getSupportedExtension()中实际是调用getExtensionClasses()方法去获取Map集合,而其底层是去从cachedClasses缓存中 获取,而adaptive扩展点实现类是缓存在了cachedAdaptiveClass中的。 @@ -385,4 +386,6 @@ public class SimpleExt$Adaptive implements org.apache.dubbo.common.extension.ext ### 3. @Activate注解 +TODO +