├── .gitignore ├── README.md ├── java-algorithm ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── wdbyte │ ├── leetcode │ ├── L001_TwoSum.java │ ├── L002_AddTwoNumbers.java │ ├── L003_LongestSubstringWithoutRepeatingCharacters.java │ └── Leetcode1047.java │ ├── offer │ ├── Lab2_Singleton.java │ ├── Lab3_FindRepeatNumber.java │ ├── Lab4_FindInTwoArray.java │ ├── Lab5_ConvertLinkedList.java │ └── Lab7_TwoStackAsQueue.java │ └── other │ ├── FindNumber.java │ ├── JiTu.java │ ├── LoadBalancingByIpHash.java │ ├── LoadBalancingByOneByOne.java │ ├── LoadBalancingByOneByOneByWeight.java │ ├── LoadBalancingByRandom.java │ ├── LoadBalancingByRandomWeight.java │ ├── SplitString.java │ ├── StringCombo.java │ └── StringSplit.java ├── java-decompiler ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── wdbyte │ └── decompiler │ ├── CFRTest.java │ ├── FernflowerTest.java │ ├── HardCode.java │ ├── JDCoreTest.java │ ├── JMHTest.java │ └── ProcyonTest.java ├── java-lab ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── wdbyte │ │ └── lab │ │ ├── classloader │ │ ├── BaseManager.java │ │ ├── ClassLoadTest.java │ │ ├── LoadInfo.java │ │ ├── ManagerFactory.java │ │ ├── MsgHandle.java │ │ ├── MyClasslLoader.java │ │ └── MyManager.java │ │ ├── io │ │ ├── aio │ │ │ ├── AioSocketServer.java │ │ │ ├── SocketClient.java │ │ │ └── SocketClientRequestThread.java │ │ ├── bio │ │ │ ├── SocketClient.java │ │ │ ├── SocketClientRequestThread.java │ │ │ ├── SocketServer.java │ │ │ └── SocketServerThread.java │ │ ├── jdknio │ │ │ ├── NioBuffer.java │ │ │ ├── NioChannel.java │ │ │ ├── NioSelector.java │ │ │ └── NioSocketServer.java │ │ └── nio │ │ │ ├── SocketServerNioListen.java │ │ │ ├── SocketServerNioListenAndRead.java │ │ │ └── SocketServerNioListenThread.java │ │ ├── jdk │ │ ├── ModCountExceptionDemo.java │ │ ├── annotation │ │ │ ├── JdkAnnotation.java │ │ │ └── PersonAnon.java │ │ └── thread │ │ │ ├── CallableFutureTest.java │ │ │ └── ExecutorTest.java │ │ ├── jvm │ │ ├── ConsumeCpu.java │ │ ├── DeadLock.java │ │ ├── MonitoringTest.java │ │ ├── OOMObject.java │ │ ├── RuntimeExec.java │ │ ├── StackOverflow.java │ │ └── SynAddRunalbe.java │ │ ├── other │ │ └── GeneratorTextImage.java │ │ ├── rmi │ │ ├── Search.java │ │ ├── SearchClient.java │ │ ├── SearchQuery.java │ │ ├── SearchServer.java │ │ └── User.java │ │ └── tree │ │ └── BitTree.java │ └── resources │ └── banner.jpg ├── java-rmi ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── wdbyte │ └── rmi │ ├── client │ └── RmiClient.java │ └── server │ ├── RmiServer.java │ ├── User.java │ ├── UserService.java │ └── UserServiceImpl.java ├── kryo-start └── src │ └── main │ └── java │ └── SomeClass.java ├── poc-log4j2 ├── Log4jTest.class ├── pom.xml └── src │ └── main │ └── java │ └── Log4j2.java ├── pom.xml ├── web-arthas ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── wdbyte │ │ └── arthas │ │ ├── Arthas.java │ │ ├── ArthasApplication.java │ │ ├── HotCode.java │ │ ├── controller │ │ └── UserController.java │ │ └── service │ │ └── UserServiceImpl.java │ └── resources │ ├── application.yml │ └── logback.xml └── web-goodskill ├── README.md ├── pom.xml └── src └── main ├── java ├── README.md └── com │ └── wdbyte │ └── goodskill │ ├── BootApplication.java │ ├── controller │ └── SkillController.java │ ├── service │ ├── SkillService.java │ └── impl │ │ └── SkillServiceImpl.java │ └── utils │ ├── KeyUtil.java │ └── RedisLock.java └── resources ├── application.yml └── logback.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_store 2 | .idea/ 3 | target/ 4 | */target/ 5 | *.iml 6 | pom.xml.tag 7 | pom.xml.releaseBackup 8 | pom.xml.versionsBackup 9 | pom.xml.next 10 | release.properties 11 | dependency-reduced-pom.xml 12 | buildNumber.properties 13 | .mvn/timing.properties 14 | 15 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 16 | !/.mvn/wrapper/maven-wrapper.jar 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Java 有趣实验室,**原创文章每周更新**。公众号首发。技术文字在写的过程中难免会有纰漏,或者细节不够完善。大家发现问题,可以及时给我 PR 反馈,也可以去 [**公众号**](https://github.com/niumoo/JavaNotes#%E5%85%AC%E4%BC%97%E5%8F%B7) 给我留言,或者加我 [微信](https://github.com/niumoo/JavaNotes#联系我) 直接说明,我都会及时更正,哪怕是一个错别字。加油!奥利给! 2 | 3 |

Lab Notes

4 |

5 | wechat 6 | 公众号 7 | CSDN 8 | 掘金 9 | 博客园 10 | 知乎 11 | 未读代码 12 |

13 | 14 | 这个仓库主要是记录一些有趣有意思的东西,以**源码**为主,每一个源码基本上都会对应一篇文章说明,文章可以在我的 [公众号](https://github.com/niumoo/JavaNotes#%E5%85%AC%E4%BC%97%E5%8F%B7) 或者仓库 [Github:niumoo/JavaNotes](https://github.com/niumoo/JavaNotes) 查看。 15 | 16 | ### ☕ 实验室 17 | - [Java 热加载的手动实现](https://github.com/niumoo/lab-notes/blob/master/java-lab/src/main/java/com/wdbyte/lab/classloader) 18 | - [Java 同步阻塞模式(Blocking IO)](https://github.com/niumoo/lab-notes/blob/master/java-lab/src/main/java/com/wdbyte/lab/io/) 19 | - [Java 同步非阻塞模式(Non-Blocking IO)](https://github.com/niumoo/lab-notes/blob/master/java-lab/src/main/java/com/wdbyte/lab/io/) 20 | - [Java 多路复用模式(Selector Non-Blocking IO)](https://github.com/niumoo/lab-notes/blob/master/java-lab/src/main/java/com/wdbyte/lab/io/) 21 | - [Java 简单的秒杀系统](https://github.com/niumoo/lab-notes/blob/master/web-goodskill/) 22 | - [Java 图片转字符图案](https://github.com/niumoo/lab-notes/blob/master/java-lab/src/main/java/com/wdbyte/lab/other/GeneratorTextImage.java) 23 | - [Java 性能分析工具使用案例 - Arthas](https://github.com/niumoo/lab-notes/blob/master/web-arthas/) 24 | - [Java 性能分析工具使用案例 - async-profiler](https://github.com/niumoo/lab-notes/blob/master/web-arthas/src/main/java/com/wdbyte/arthas/HotCode.java) 25 | - [常见的负载均衡算法](https://github.com/niumoo/lab-notes/blob/master/java-algorithm/src/main/java/com/wdbyte/other/) 26 | - [三种并发修改异常的绕过方式](https://github.com/niumoo/lab-notes/blob/master/java-lab/src/main/java/com/wdbyte/lab/jdk/ModCountExceptionDemo.java) 27 | 28 | ### 🍭 剑指Offer算法题目 29 | - [剑指 Offer 002:单例模式](https://github.com/niumoo/lab-notes/blob/master/java-algorithm/src/main/java/com/wdbyte/offer/) 30 | - [剑指 Offer 003:查找重复数字](https://github.com/niumoo/lab-notes/blob/master/java-algorithm/src/main/java/com/wdbyte/offer/) 31 | - [剑指 Offer 004:矩阵查找数字是否存在](https://github.com/niumoo/lab-notes/blob/master/java-algorithm/src/main/java/com/wdbyte/offer/) 32 | - [剑指 Offer 005:反转链表](https://github.com/niumoo/lab-notes/blob/master/java-algorithm/src/main/java/com/wdbyte/offer/) 33 | - [剑指 Offer 007:两个栈实现一个队列](https://github.com/niumoo/lab-notes/blob/master/java-algorithm/src/main/java/com/wdbyte/offer/) 34 | 35 | ### ⏳ 小算法 36 | - [字符串均匀分批](https://github.com/niumoo/lab-notes/blob/master/java-algorithm/src/main/java/com/wdbyte/other) 37 | - [字符串排列组合](https://github.com/niumoo/lab-notes/blob/master/java-algorithm/src/main/java/com/wdbyte/other) 38 | 39 | ### 联系我 40 | 41 | 可以添加我的微信 wn8398 一起交流。 42 | 43 | 微信 44 | 45 | ### 公众号 46 | 47 | 有帮助可以点「**赞**」在看或 :star: **Star**,谢谢你! 48 | 49 | 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注「 **未读代码** 」公众号。 50 | 51 | ![公众号](https://cdn.jsdelivr.net/gh/niumoo/cdn-assets/webinfo/weixin-public.jpg) 52 | -------------------------------------------------------------------------------- /java-algorithm/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | lab-notes 7 | com.wdbyte 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | java-algorithm 13 | 14 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/leetcode/L001_TwoSum.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.leetcode; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | *

7 | * 给定一个整数数组,返回两个数字的索引,使它们相加到特定目标。 您可以假设每个输入只有一个解决方案,并且您可能不会两次使用相同的元素。 8 | * Example: 9 | * Given nums = [2, 7, 11, 15], target = 9, 10 | * Because nums[0] + nums[1] = 2 + 7 = 9, 11 | * return [0, 1]. 12 | * 13 | * @Author niujinpeng 14 | * @Date 2018/10/31 18:12 15 | */ 16 | public class L001_TwoSum { 17 | 18 | public static void main(String[] args) { 19 | int[] nums = {2, 7, 11, 15}; 20 | int target = 9; 21 | // int[] ints = new TwoSum().twoSum(nums, target); 22 | int[] ints = new L001_TwoSum().twoSumByHash(nums, target); 23 | for (int anInt : ints) { 24 | System.out.println(anInt); 25 | 26 | } 27 | } 28 | 29 | /** 30 | * 暴力破解法 31 | * 32 | * @param nums 33 | * @param target 34 | * @return 35 | */ 36 | public int[] twoSum(int[] nums, int target) { 37 | for (int i = 0; i < nums.length; i++) { 38 | for (int j = i + 1; j < nums.length; j++) { 39 | if ((nums[i] + nums[j]) == target) { 40 | int[] result = {i, j}; 41 | return result; 42 | } 43 | } 44 | } 45 | return null; 46 | } 47 | 48 | /** 49 | * HashMap方法 50 | * 99.3% over 51 | * 52 | * @param nums 53 | * @param target 54 | * @return 55 | */ 56 | public int[] twoSumByHash(int[] nums, int target) { 57 | HashMap hashMap = new HashMap<>(); 58 | for (int i = 0; i < nums.length; i++) { 59 | if (hashMap.containsKey(target - nums[i])) { 60 | Integer value = hashMap.get(target - nums[i]); 61 | return new int[] {value, i}; 62 | } 63 | hashMap.put(nums[i], i); 64 | } 65 | return null; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/leetcode/L002_AddTwoNumbers.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.leetcode; 2 | 3 | /** 4 | *

5 | * 您将获得两个非空链表,表示两个非负整数。数字以相反的顺序存储, 6 | * 每个节点包含一个数字。添加两个数字并将其作为链接列表返回。 您 7 | * 可以假设这两个数字不包含任何前导零,除了数字0本身。 8 | *

9 | * Example: 10 | * Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) 11 | * Output: 7 -> 0 -> 8 12 | * Explanation: 342 + 465 = 807. 13 | * 14 | * @Author niujinpeng 15 | * @Date 2018/11/1 16:56 16 | */ 17 | public class L002_AddTwoNumbers { 18 | 19 | public static void main(String[] args) { 20 | ListNode l1 = new ListNode(2); 21 | ListNode l11 = new ListNode(4); 22 | ListNode l111 = new ListNode(3); 23 | l11.next = l111; 24 | l1.next = l11; 25 | 26 | ListNode l2 = new ListNode(5); 27 | ListNode l22 = new ListNode(6); 28 | ListNode l222 = new ListNode(4); 29 | l22.next = l222; 30 | l2.next = l22; 31 | 32 | ListNode listRes = new L002_AddTwoNumbers().addTwoNumbers2(l1, l2); 33 | while (listRes != null) { 34 | System.out.print(listRes.val); 35 | listRes = listRes.next; 36 | } 37 | 38 | } 39 | 40 | /** 41 | * 算数解法 42 | * 像小学算术一样去解决这个问题, 43 | * isCarry用于记录溢出位 44 | * 45 | * @param l1 46 | * @param l2 47 | * @return 48 | */ 49 | public ListNode addTwoNumbers2(ListNode l1, ListNode l2) { 50 | ListNode listRes = null; 51 | ListNode listTemp = null; 52 | boolean isCarry = false; 53 | while (l1 != null || l2 != null) { 54 | int n1 = l1 != null ? l1.val : 0; 55 | int n2 = l2 != null ? l2.val : 0; 56 | int value = n1 + n2; 57 | if (isCarry) { 58 | value++; 59 | isCarry = false; 60 | } 61 | if (value > 9) { 62 | isCarry = true; 63 | value = value % 10; 64 | } 65 | ListNode listNode = new ListNode(value); 66 | if (listRes == null) { 67 | listRes = listNode; 68 | } else { 69 | listTemp.next = listNode; 70 | } 71 | listTemp = listNode; 72 | l1 = l1 != null ? l1.next : null; 73 | l2 = l2 != null ? l2.next : null; 74 | } 75 | if (isCarry) { 76 | listTemp.next = new ListNode(1); 77 | } 78 | return listRes; 79 | } 80 | } 81 | 82 | class ListNode { 83 | int val; 84 | ListNode next; 85 | 86 | ListNode(int x) { 87 | val = x; 88 | } 89 | } -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/leetcode/L003_LongestSubstringWithoutRepeatingCharacters.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.leetcode; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | /** 7 | * 没有重复字符的最长子串 8 | *

9 | * 给定一个字符串,找到最长子字符串的长度而不重复字符。 10 | *

11 | * Example 1: 12 | *

13 | * Input: "abcabcbb" 14 | * Output: 3 15 | * Explanation: The answer is "abc", with the length of 3. 16 | * Example 2: 17 | *

18 | * Input: "bbbbb" 19 | * Output: 1 20 | * Explanation: The answer is "b", with the length of 1. 21 | * Example 3: 22 | *

23 | * Input: "pwwkew" 24 | * Output: 3 25 | * Explanation: The answer is "wke", with the length of 3. 26 | * Note that the answer must be a substring, "pwke" is a subsequence and not a substring. 27 | * 28 | * @Author niujinpeng 29 | * @Date 2018/11/2 17:51 30 | */ 31 | public class L003_LongestSubstringWithoutRepeatingCharacters { 32 | public static void main(String[] args) { 33 | // int length = lengthOfLongestSubstringByChar("abcabcbb"); 34 | // System.out.println(length); 35 | } 36 | 37 | /** 38 | * 找最大子串,如果已保存的结果中有相同的,清除,然后报错最大长度。 39 | * 40 | * @param s 41 | * @return 42 | */ 43 | public static int lengthOfLongestSubstring(String s) { 44 | if (s.length() == 0) { 45 | return 0; 46 | } 47 | String[] sarr = s.split(""); 48 | int max = 0; 49 | LinkedList list = new LinkedList<>(); 50 | for (String s1 : sarr) { 51 | if (list.contains(s1)) { 52 | while (!list.isEmpty()) { 53 | if (list.get(0).equals(s1)) { 54 | list.remove(0); 55 | break; 56 | } 57 | list.remove(0); 58 | } 59 | } 60 | list.add(s1); 61 | max = list.size() > max ? list.size() : max; 62 | } 63 | return max; 64 | } 65 | 66 | /** 67 | * 使用字符串解决 68 | * 69 | * @param s 70 | * @return 71 | */ 72 | public static int lengthOfLongestSubstringByString(String s) { 73 | int max = 0; 74 | int start = 0; 75 | for (int i = 0; i < s.length(); i++) { 76 | // 当前字符 77 | String ch = s.substring(i, i + 1); 78 | // 缓存字符串 79 | String tempStr = s.substring(start, i); 80 | // 缓存包含当前字符 81 | if (tempStr.contains(ch)) { 82 | // 找到字符去除 83 | int indexOf = tempStr.indexOf(ch); 84 | start = indexOf + 1; 85 | } 86 | max = (i + 1) - start > max ? (i + 1) - start : max; 87 | } 88 | return max; 89 | } 90 | 91 | public static int lengthOfLongestSubstringByHashSet(String s) { 92 | int max = 0; 93 | List list = new LinkedList<>(); 94 | for (int i = 0; i < s.length(); i++) { 95 | char c = s.charAt(i); 96 | if (!list.contains(c)) { 97 | list.add(c); 98 | max = Math.max(list.size(), max); 99 | } else { 100 | int indexOf = list.indexOf(c); 101 | list = list.subList(indexOf + 1, list.size()); 102 | list.add(c); 103 | } 104 | } 105 | return max; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/leetcode/Leetcode1047.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.leetcode; 2 | 3 | import java.util.List; 4 | import java.util.Stack; 5 | import java.util.stream.Collectors; 6 | 7 | /** 8 | * 给出由小写字母组成的字符串S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。 9 | * 10 | * 在 S 上反复执行重复项删除操作,直到无法继续删除。 11 | * 12 | * 在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。 13 | * 14 | * 示例: 15 | * 16 | * 输入:"abbaca" 17 | * 输出:"ca" 18 | * 解释: 19 | * 例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。 20 | * 21 | * 来源:力扣(LeetCode) 22 | * 链接:https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string 23 | * 24 | * @author github.com/niumoo 25 | * @date 2021/03/09 26 | */ 27 | public class Leetcode1047 { 28 | 29 | public static void main(String[] args) { 30 | System.out.println(new Leetcode1047().removeDuplicates("acbbaaca")); 31 | System.out.println(new Leetcode1047().removeDuplicates2("abbaca")); 32 | System.out.println(new Leetcode224().calculate2(" 2-1 + 2 ")); 33 | } 34 | 35 | /** 36 | * 执行结果: 37 | * 通过 38 | * 显示详情 39 | * 执行用时: 40 | * 51 ms 41 | * , 在所有 Java 提交中击败了 42 | * 20.05% 43 | * 的用户 44 | * 内存消耗: 45 | * 41.4 MB 46 | * , 在所有 Java 提交中击败了 47 | * 5.09% 48 | * 的用户 49 | * @param s 50 | * @return 51 | */ 52 | public String removeDuplicates(String s) { 53 | char[] chars = s.toCharArray(); 54 | Stack stack = new Stack<>(); 55 | for (Character character : chars) { 56 | if (!stack.isEmpty() && character == stack.peek()) { 57 | stack.pop(); 58 | } else { 59 | stack.add(character); 60 | } 61 | } 62 | List collect = stack.stream().map(String::valueOf).collect(Collectors.toList()); 63 | String join = String.join("", collect); 64 | return join; 65 | } 66 | 67 | /** 68 | * 执行用时: 69 | * 13 ms 70 | * , 在所有 Java 提交中击败了 71 | * 77.82% 72 | * 的用户 73 | * 内存消耗: 74 | * 38.9 MB 75 | * , 在所有 Java 提交中击败了 76 | * 75.90% 77 | * 的用户 78 | * @param s 79 | * @return 80 | */ 81 | public String removeDuplicates2(String s) { 82 | StringBuffer buffer = new StringBuffer(); 83 | int index = -1; 84 | for (int i = 0; i < s.length(); i++) { 85 | char c = s.charAt(i); 86 | if (index != -1 && c == buffer.charAt(index)) { 87 | buffer.deleteCharAt(index); 88 | index--; 89 | } else { 90 | buffer.append(c); 91 | index++; 92 | } 93 | } 94 | return buffer.toString(); 95 | } 96 | } -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/offer/Lab2_Singleton.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.offer; 2 | 3 | /** 4 | *

5 | * 剑指office第二题 6 | * Java 单例模式 7 | * 8 | * @Author niujinpeng 9 | * @Date 2019/3/21 22:22 10 | */ 11 | public class Lab2_Singleton { 12 | public static void main(String[] args) { 13 | Singleton singleton = Singleton.getSingleton(); 14 | Singleton2 singleton2 = Singleton2.getSingleton2(); 15 | } 16 | } 17 | 18 | /** 19 | * 懒汉模式,延迟初始化 20 | */ 21 | class Singleton { 22 | private Singleton() { 23 | } 24 | 25 | private static class SingletonHolder { 26 | static final Singleton INSTANCE = new Singleton(); 27 | } 28 | 29 | public static Singleton getSingleton() { 30 | return SingletonHolder.INSTANCE; 31 | } 32 | } 33 | 34 | /** 35 | * 饿汉模式,直接初始化 36 | */ 37 | class Singleton2 { 38 | private static Singleton2 singleton2 = new Singleton2(); 39 | 40 | private Singleton2() { 41 | } 42 | 43 | public static Singleton2 getSingleton2() { 44 | return singleton2; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/offer/Lab3_FindRepeatNumber.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.offer; 2 | 3 | /** 4 | *

5 | * 剑指office第三题 6 | *

7 | * 在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的, 8 | *

9 | * 但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 10 | * 11 | * @Author niujinpeng 12 | * @Date 2019/3/8 17:53 13 | */ 14 | public class Lab3_FindRepeatNumber { 15 | public static void main(String[] args) { 16 | int[] arr = {2, 3, 1, 0, 2, 5}; 17 | solution(arr); 18 | } 19 | 20 | /** 21 | * 把数字放到值对应的坐标上,如果坐标上已经存在相同值。说明存在重复 22 | * 23 | * @param arr 24 | */ 25 | public static void solution(int[] arr) { 26 | if (arr == null || arr.length < 1) { 27 | return; 28 | } 29 | for (int i = 0; i < arr.length; i++) { 30 | // 当前值 31 | while (arr[i] != i) { 32 | int value = arr[i]; 33 | if (arr[value] == value) { 34 | System.out.println("发现重复值" + value); 35 | return; 36 | } else { 37 | // swap 38 | int temp = arr[value]; 39 | arr[value] = value; 40 | arr[i] = temp; 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/offer/Lab4_FindInTwoArray.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.offer; 2 | 3 | /** 4 | *

5 | * 剑指office第四题 6 | * 7 | * 在一个NXN的矩阵中查找是否存在某个数字,矩阵数据每一行从左向右从小到大,从上到下从小到大 8 | * 9 | * 10 | * @Author niujinpeng 11 | * @Date 2019/3/22 13:36 12 | */ 13 | public class Lab4_FindInTwoArray { 14 | 15 | public static void main(String[] args) { 16 | solution(100); 17 | } 18 | 19 | public static boolean solution(int target) { 20 | // 初始化数组 21 | int[] line1 = {1, 2, 3, 4, 5}; 22 | int[] line2 = {2, 3, 4, 5, 6}; 23 | int[] line3 = {5, 6, 7, 8, 9}; 24 | int[] line4 = {7, 8, 10, 14, 16}; 25 | int[] line5 = {9, 11, 13, 15, 18}; 26 | int[][] arr = {line1, line2, line3, line4, line5}; 27 | 28 | int height = arr[0].length - 1; 29 | int width = 0; 30 | int value = arr[width][height]; 31 | while (value != target) { 32 | if (value > target) { 33 | height--; 34 | } else { 35 | width++; 36 | } 37 | if (height < 0 || width == arr[0].length) { 38 | System.out.println("不存在这个数字"); 39 | return false; 40 | } 41 | value = arr[width][height]; 42 | } 43 | System.out.println("存在于[" + width + "][" + height + "]"); 44 | return true; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/offer/Lab5_ConvertLinkedList.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.offer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | *

7 | * 剑指office 反转链表 8 | * 9 | * @Author niujinpeng 10 | * @Date 2019/3/22 14:13 11 | */ 12 | public class Lab5_ConvertLinkedList { 13 | 14 | public static void main(String[] args) { 15 | Linked linked = new Linked(1, new Linked(2, new Linked(3, new Linked(4, null)))); 16 | solution(linked); 17 | } 18 | 19 | /** 20 | * 倒序输出链表 遍历链表入栈,然后出栈。 21 | * 22 | * @param linked 23 | */ 24 | public static void solution(Linked linked) { 25 | Stack stack = new Stack<>(); 26 | while (linked != null) { 27 | stack.push(linked.getData()); 28 | linked = linked.getNext(); 29 | } 30 | while (stack.size() != 0) { 31 | System.out.println(stack.pop()); 32 | } 33 | } 34 | } 35 | 36 | class Linked { 37 | private int data; 38 | private Linked next; 39 | 40 | public Linked(int data, Linked next) { 41 | this.data = data; 42 | this.next = next; 43 | } 44 | 45 | public int getData() { 46 | return data; 47 | } 48 | 49 | public void setData(int data) { 50 | this.data = data; 51 | } 52 | 53 | public Linked getNext() { 54 | return next; 55 | } 56 | 57 | public void setNext(Linked next) { 58 | this.next = next; 59 | } 60 | } -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/offer/Lab7_TwoStackAsQueue.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.offer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | *

7 | * 两个栈实现一个队列 8 | * 9 | * @Author niujinpeng 10 | * @Date 2019/3/22 14:32 11 | */ 12 | public class Lab7_TwoStackAsQueue { 13 | public static void main(String[] args) { 14 | Queue queue = new Queue(); 15 | queue.push(1); 16 | queue.push(2); 17 | queue.push(3); 18 | queue.push(4); 19 | while (true) { 20 | System.out.println(queue.pop()); 21 | } 22 | 23 | } 24 | 25 | public static class Queue { 26 | Stack stack1 = new Stack(); 27 | Stack stack2 = new Stack(); 28 | 29 | public void push(int node) { 30 | stack1.push(node); 31 | } 32 | 33 | public int pop() { 34 | if (stack1.isEmpty() && stack2.isEmpty()) { 35 | throw new RuntimeException("queue is empty"); 36 | } 37 | if (stack2.isEmpty()) { 38 | while (!stack1.isEmpty()) { 39 | stack2.push(stack1.pop()); 40 | } 41 | } 42 | return stack2.pop(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/FindNumber.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | *

8 | * 9 | * @Author niujinpeng 10 | * @Date 2018/11/28 15:15 11 | */ 12 | public class FindNumber { 13 | 14 | public static void main(String[] args) { 15 | long star = System.currentTimeMillis(); 16 | int length = 400000000; 17 | byte[] bytes = new byte[length]; 18 | for (int i = 0; i < length; i++) { 19 | bytes[i] = 1; 20 | } 21 | System.out.println(bytes[65893696]); 22 | long end = System.currentTimeMillis(); 23 | System.out.println("执行时间:" + (end - star) + "ms"); 24 | } 25 | 26 | 27 | public void hashMapTest() { 28 | long star = System.currentTimeMillis(); 29 | Set hashset = new HashSet<>(100); 30 | for (int i = 0; i < 10000000; i++) { 31 | hashset.add(i); 32 | } 33 | System.out.println(hashset.contains(9999)); 34 | //System.out.println(hashset.contains(2)); 35 | //System.out.println(hashset.contains(3)); 36 | long end = System.currentTimeMillis(); 37 | System.out.println("执行时间:" + (end - star) + "ms"); 38 | } 39 | 40 | public void bitTest() { 41 | long star = System.currentTimeMillis(); 42 | int length = 400000000; 43 | byte[] bytes = new byte[length]; 44 | Set hashset = new HashSet<>(100); 45 | for (int i = 0; i < length; i++) { 46 | bytes[i] = 1; 47 | } 48 | System.out.println(bytes[65893696]); 49 | long end = System.currentTimeMillis(); 50 | System.out.println("执行时间:" + (end - star) + "ms"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/JiTu.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | /** 4 | *

5 | * 6 | * @Author niujinpeng 7 | * @Date 2019/11/6 23:23 8 | */ 9 | public class JiTu { 10 | 11 | public static void main(String[] args) { 12 | /** 13 | * 数学解法 14 | * 兔子 x 个,鸡 y 个, 15 | * x + y = n; 16 | * 4x + 2y = m; 17 | * 4(n - y) + 2y = m; 18 | * 4n - 4y + 2y = m; 19 | * 4n - 2y = m; 20 | * 4n - m = 2y; 21 | * (4n - m)/2 = y 22 | */ 23 | int n = 10; 24 | int m = 32; 25 | int ji = (4 * n - m) / 2; 26 | int tuzi = n - ji; 27 | if (ji > 0 && ji <= n) { 28 | System.out.println("兔子:" + tuzi + " 鸡:" + ji); 29 | } else { 30 | System.out.println("ERROR"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/LoadBalancingByIpHash.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Random; 8 | 9 | /** 10 | *

11 | * 负载均衡算法 - IP Hash 12 | * 13 | * @author niujinpeng 14 | * @Date 2020/5/26 23:06 15 | */ 16 | public class LoadBalancingByIpHash { 17 | 18 | private static List serverList = new ArrayList<>(); 19 | static { 20 | serverList.add("192.168.1.2"); 21 | serverList.add("192.168.1.3"); 22 | serverList.add("192.168.1.4"); 23 | serverList.add("192.168.1.5"); 24 | } 25 | 26 | /** 27 | * ip hash 路由算法 28 | */ 29 | public static String ipHash(String ip) { 30 | // 复制遍历用的集合,防止操作中集合有变更 31 | List tempList = new ArrayList<>(serverList.size()); 32 | tempList.addAll(serverList); 33 | int index = ip.hashCode() % serverList.size(); 34 | return tempList.get(Math.abs(index)); 35 | } 36 | 37 | public static void main(String[] args) { 38 | HashMap serverMap = new HashMap<>(); 39 | for (int i = 0; i < 100000; i++) { 40 | String server = ipHash(generateIp()); 41 | Integer count = serverMap.get(server); 42 | if (count == null) { 43 | count = 1; 44 | } else { 45 | count++; 46 | } 47 | // 记录 48 | serverMap.put(server, count); 49 | } 50 | // 路由总体结果 51 | for (Map.Entry entry : serverMap.entrySet()) { 52 | System.out.println("IP:" + entry.getKey() + ",次数:" + entry.getValue()); 53 | } 54 | } 55 | 56 | 57 | 58 | public static String generateIp() { 59 | Random random = new Random(); 60 | int ip1 = random.nextInt(255); 61 | int ip2 = random.nextInt(255); 62 | int ip3 = random.nextInt(255); 63 | int ip4 = random.nextInt(255); 64 | // 例如:192.111.123.123 65 | return ip1 + "." + ip2 + "." + ip3 + "." + ip4; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/LoadBalancingByOneByOne.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | *

10 | * 负载均衡算法 - 轮训 11 | * 12 | * @author niujinpeng 13 | * @Date 2020/5/26 23:06 14 | */ 15 | public class LoadBalancingByOneByOne { 16 | 17 | /** 服务器列表 */ 18 | private static List serverList = new ArrayList<>(); 19 | static { 20 | serverList.add("192.168.1.2"); 21 | serverList.add("192.168.1.3"); 22 | serverList.add("192.168.1.4"); 23 | serverList.add("192.168.1.5"); 24 | } 25 | private static Integer index = 0; 26 | 27 | /** 28 | * 随机路由算法 29 | */ 30 | public static String randomOneByOne() { 31 | // 复制遍历用的集合,防止操作中集合有变更 32 | List tempList = new ArrayList<>(serverList.size()); 33 | tempList.addAll(serverList); 34 | String server = ""; 35 | synchronized (index) { 36 | index++; 37 | if (index == tempList.size()) { 38 | index = 0; 39 | } 40 | server = tempList.get(index);; 41 | } 42 | return server; 43 | } 44 | 45 | public static void main(String[] args) { 46 | HashMap serverMap = new HashMap<>(); 47 | for (int i = 0; i < 100000; i++) { 48 | String server = randomOneByOne(); 49 | Integer count = serverMap.get(server); 50 | if (count == null) { 51 | count = 1; 52 | } else { 53 | count++; 54 | } 55 | // 记录 56 | serverMap.put(server, count); 57 | } 58 | // 路由总体结果 59 | for (Map.Entry entry : serverMap.entrySet()) { 60 | System.out.println("IP:" + entry.getKey() + ",次数:" + entry.getValue()); 61 | } 62 | } 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/LoadBalancingByOneByOneByWeight.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | *

10 | * 负载均衡算法 - 加权轮训 11 | * 12 | * @author niujinpeng 13 | * @Date 2020/5/26 23:06 14 | */ 15 | public class LoadBalancingByOneByOneByWeight { 16 | /** 服务器列表 */ 17 | private static HashMap serverMap = new HashMap<>(); 18 | static { 19 | serverMap.put("192.168.1.2", 2); 20 | serverMap.put("192.168.1.3", 2); 21 | serverMap.put("192.168.1.4", 2); 22 | serverMap.put("192.168.1.5", 4); 23 | } 24 | private static Integer index = 0; 25 | 26 | /** 27 | * 加权路由算法 28 | */ 29 | public static String oneByOneWithWeight() { 30 | List tempList = new ArrayList(); 31 | HashMap tempMap = new HashMap<>(); 32 | tempMap.putAll(serverMap); 33 | for (String key : serverMap.keySet()) { 34 | for (int i = 0; i < serverMap.get(key); i++) { 35 | tempList.add(key); 36 | } 37 | } 38 | synchronized (index) { 39 | index++; 40 | if (index == tempList.size()) { 41 | index = 0; 42 | } 43 | return tempList.get(index); 44 | } 45 | } 46 | 47 | public static void main(String[] args) { 48 | HashMap serverMap = new HashMap<>(); 49 | for (int i = 0; i < 100000; i++) { 50 | String server = oneByOneWithWeight(); 51 | Integer count = serverMap.get(server); 52 | if (count == null) { 53 | count = 1; 54 | } else { 55 | count++; 56 | } 57 | // 记录 58 | serverMap.put(server, count); 59 | } 60 | // 路由总体结果 61 | for (Map.Entry entry : serverMap.entrySet()) { 62 | System.out.println("IP:" + entry.getKey() + ",次数:" + entry.getValue()); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/LoadBalancingByRandom.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Random; 8 | 9 | /** 10 | *

11 | * 负载均衡算法 - 随机 12 | * 13 | * @author niujinpeng 14 | * @Date 2020/5/26 23:06 15 | */ 16 | public class LoadBalancingByRandom { 17 | 18 | /** 服务器列表 */ 19 | private static List serverList = new ArrayList<>(); 20 | static { 21 | serverList.add("192.168.1.2"); 22 | serverList.add("192.168.1.3"); 23 | serverList.add("192.168.1.4"); 24 | serverList.add("192.168.1.5"); 25 | } 26 | 27 | /** 28 | * 随机路由算法 29 | */ 30 | public static String random() { 31 | // 复制遍历用的集合,防止操作中集合有变更 32 | List tempList = new ArrayList<>(serverList.size()); 33 | tempList.addAll(serverList); 34 | // 随机数随机访问 35 | int randomInt = new Random().nextInt(tempList.size()); 36 | return tempList.get(randomInt); 37 | } 38 | 39 | public static void main(String[] args) { 40 | HashMap serverMap = new HashMap<>(); 41 | for (int i = 0; i < 100000; i++) { 42 | String server = random(); 43 | Integer count = serverMap.get(server); 44 | if (count == null) { 45 | count = 1; 46 | } else { 47 | count++; 48 | } 49 | // 记录 50 | serverMap.put(server, count); 51 | } 52 | // 路由总体结果 53 | for (Map.Entry entry : serverMap.entrySet()) { 54 | System.out.println("IP:" + entry.getKey() + ",次数:" + entry.getValue()); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/LoadBalancingByRandomWeight.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Random; 8 | 9 | /** 10 | *

11 | * 负载均衡算法 - 加权随机 12 | * 13 | * @author niujinpeng 14 | * @Date 2020/5/26 23:06 15 | */ 16 | public class LoadBalancingByRandomWeight { 17 | /** 服务器列表 */ 18 | private static HashMap serverMap = new HashMap<>(); 19 | static { 20 | serverMap.put("192.168.1.2", 2); 21 | serverMap.put("192.168.1.3", 2); 22 | serverMap.put("192.168.1.4", 2); 23 | serverMap.put("192.168.1.5", 4); 24 | } 25 | /** 26 | * 加权路由算法 27 | */ 28 | public static String randomWithWeight() { 29 | List tempList = new ArrayList(); 30 | HashMap tempMap = new HashMap<>(); 31 | tempMap.putAll(serverMap); 32 | for (String key : serverMap.keySet()) { 33 | for (int i = 0; i < serverMap.get(key); i++) { 34 | tempList.add(key); 35 | } 36 | } 37 | int randomInt = new Random().nextInt(tempList.size()); 38 | return tempList.get(randomInt); 39 | } 40 | 41 | public static void main(String[] args) { 42 | HashMap serverMap = new HashMap<>(); 43 | for (int i = 0; i < 100000; i++) { 44 | String server = randomWithWeight(); 45 | Integer count = serverMap.get(server); 46 | if (count == null) { 47 | count = 1; 48 | } else { 49 | count++; 50 | } 51 | // 记录 52 | serverMap.put(server, count); 53 | } 54 | // 路由总体结果 55 | for (Map.Entry entry : serverMap.entrySet()) { 56 | System.out.println("IP:" + entry.getKey() + ",次数:" + entry.getValue()); 57 | } 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/SplitString.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.ArrayList; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | /** 8 | *

9 | * 根据指定字符串切分字符串 10 | * 11 | * @Author niujinpeng 12 | * @Date 2019/1/28 17:11 13 | */ 14 | public class SplitString { 15 | 16 | public static void main(String[] args) { 17 | String[] strings = split("123a123a3242a42432a1231a", "a"); 18 | for (String string : strings) { 19 | System.out.println(string); 20 | } 21 | 22 | } 23 | 24 | public static String[] split(String source, String str) { 25 | 26 | if (StringUtils.isEmpty(source)) { 27 | return null; 28 | } 29 | ArrayList arrayList = new ArrayList<>(); 30 | if (StringUtils.isEmpty(str)) { 31 | arrayList.add(source); 32 | String[] strings = (String[]) arrayList.toArray(); 33 | return strings; 34 | } 35 | 36 | char[] sourceChars = source.toCharArray(); 37 | char[] chars = str.toCharArray(); 38 | String temp = new String(); 39 | // 分割字符只有一位 40 | if (chars.length == 1) { 41 | for (char aChar : sourceChars) { 42 | if (aChar == chars[0] && StringUtils.isNotEmpty(temp)) { 43 | arrayList.add(temp); 44 | temp = new String(); 45 | } else { 46 | temp = temp + aChar; 47 | } 48 | } 49 | } else { 50 | for (int i = 0; i < sourceChars.length; i++) { 51 | char aChar = sourceChars[i]; 52 | if (aChar == chars[0] && StringUtils.isNotEmpty(temp)) { 53 | 54 | arrayList.add(temp); 55 | temp = new String(); 56 | } else { 57 | temp = temp + aChar; 58 | } 59 | 60 | } 61 | } 62 | String[] strings = new String[arrayList.size()]; 63 | arrayList.toArray(strings); 64 | return strings; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/StringCombo.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | /** 9 | *

10 | * 给定一个不存在重复字符的字符串,打印出该字符串的所有排列组合。 11 | * 例如,输入字符串”abc”,打印:”abc”,”acb”,”bac”,”bca”,”cab”,”cba” 12 | * 13 | * @author niujinpeng 14 | * @Date 2020/6/24 14:57 15 | */ 16 | @Slf4j 17 | public class StringCombo { 18 | 19 | public static void main(String[] args) { 20 | String content = "ABC"; 21 | List list = comboString(content); 22 | for (String s : list) { 23 | System.out.println(s); 24 | } 25 | } 26 | 27 | public static List comboString(String str) { 28 | List resultList = new ArrayList<>(); 29 | if (str.length() == 1) { 30 | resultList.add(str); 31 | return resultList; 32 | } 33 | List list = new ArrayList<>(); 34 | for (char c : str.toCharArray()) { 35 | list.add(String.valueOf(c)); 36 | } 37 | List listTemp = new ArrayList<>(); 38 | for (int i = 0; i < list.size(); i++) { 39 | listTemp.clear(); 40 | listTemp.addAll(list); 41 | String remove = listTemp.remove(i); 42 | StringBuffer sb = new StringBuffer(); 43 | for (String s : listTemp) { 44 | sb.append(s); 45 | } 46 | List stringList = comboString(sb.toString()); 47 | for (String s : stringList) { 48 | resultList.add(remove + s); 49 | } 50 | } 51 | return resultList; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /java-algorithm/src/main/java/com/wdbyte/other/StringSplit.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.other; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | *

8 | * 字符串分批 9 | *

10 | * 给定一个存储了不重复字符串的集合,以及一个分批数,对字符串集合进行尽可能均匀的分批 11 | * 例如:字符串集合为["a", "b", "c"],分批数为 2,则返回:[["a", "b"], ["c"]] 12 | * 注:返回 [["a"], ["b", "c"]] 也可以 13 | * 14 | * @author niujinpeng 15 | * @Date 2020/6/23 8:19 16 | */ 17 | public class StringSplit { 18 | 19 | public static void main(String[] args) { 20 | String content = "1234567890"; 21 | int size = 6; 22 | List stringList = split(content, size); 23 | for (String s : stringList) { 24 | System.out.println(s); 25 | } 26 | } 27 | 28 | public static List split(String content, int size) { 29 | List list = new ArrayList<>(); 30 | int yu = content.length() % size; 31 | int sizeTemp = content.length() / size; 32 | int index = 0; 33 | for (int i = 0; i < size; i++) { 34 | if (i < yu) { 35 | String result = content.substring(index, index + sizeTemp + 1); 36 | index = index + sizeTemp + 1; 37 | list.add(result); 38 | } else { 39 | String result = content.substring(index, index + sizeTemp); 40 | index = index + sizeTemp; 41 | list.add(result); 42 | } 43 | } 44 | return list; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /java-decompiler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.wdbyte 8 | java-decompiler 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | 17 | 18 | org.openjdk.jmh 19 | jmh-core 20 | 1.23 21 | 22 | 23 | org.openjdk.jmh 24 | jmh-generator-annprocess 25 | 1.23 26 | provided 27 | 28 | 29 | 30 | 31 | org.jboss.windup.decompiler 32 | decompiler-procyon 33 | 5.1.4.Final 34 | 35 | 36 | 37 | 38 | org.jboss.windup.decompiler 39 | decompiler-fernflower 40 | 5.1.4.Final 41 | 42 | 43 | 44 | 45 | org.benf 46 | cfr 47 | 0.151 48 | 49 | 50 | 51 | 52 | org.jd 53 | jd-core 54 | 1.1.3 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /java-decompiler/src/main/java/com/wdbyte/decompiler/CFRTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.decompiler; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | 8 | import org.benf.cfr.reader.api.CfrDriver; 9 | import org.benf.cfr.reader.util.getopt.OptionsImpl; 10 | 11 | /** 12 | * CFR Test 13 | * 14 | * @author https://github.com/niumoo 15 | * @date 2021/05/15 16 | */ 17 | public class CFRTest { 18 | 19 | public static void main(String[] args) throws IOException { 20 | Long time = cfr("decompiler.jar", "./cfr_output_jar"); 21 | System.out.println(String.format("decompiler time: %dms", time)); 22 | } 23 | 24 | public static Long cfr(String source, String targetPath) throws IOException { 25 | Long start = System.currentTimeMillis(); 26 | // source jar 27 | List files = new ArrayList<>(); 28 | files.add(source); 29 | // target dir 30 | HashMap outputMap = new HashMap<>(); 31 | outputMap.put("outputdir", targetPath); 32 | 33 | OptionsImpl options = new OptionsImpl(outputMap); 34 | CfrDriver cfrDriver = new CfrDriver.Builder().withBuiltOptions(options).build(); 35 | cfrDriver.analyse(files); 36 | Long end = System.currentTimeMillis(); 37 | return (end - start); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /java-decompiler/src/main/java/com/wdbyte/decompiler/FernflowerTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.decompiler; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Path; 5 | import java.nio.file.Paths; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | import org.jboss.windup.decompiler.api.DecompilationFailure; 10 | import org.jboss.windup.decompiler.api.DecompilationListener; 11 | import org.jboss.windup.decompiler.api.DecompilationResult; 12 | import org.jboss.windup.decompiler.fernflower.FernflowerDecompiler; 13 | 14 | /** 15 | * @author darcy 16 | * @Date 2021/05/16 17 | */ 18 | public class FernflowerTest { 19 | 20 | public static void main(String[] args) { 21 | Long time = fernflower("decompiler.jar", "fernflower_output_jar"); 22 | System.out.println(String.format("decompiler time: %dms", time)); 23 | } 24 | 25 | public static Long fernflower(String source, String targetPath) { 26 | long start = System.currentTimeMillis(); 27 | Path outDir = Paths.get(targetPath); 28 | Path archive = Paths.get(source); 29 | FernflowerDecompiler decompiler = new FernflowerDecompiler(); 30 | DecompilationResult res = decompiler.decompileArchive(archive, outDir, new DecompilationListener() { 31 | public void decompilationProcessComplete() { 32 | System.out.println("decompilationProcessComplete"); 33 | } 34 | public void decompilationFailed(List inputPath, String message) { 35 | System.out.println("decompilationFailed"); 36 | } 37 | public void fileDecompiled(List inputPath, String outputPath) { 38 | } 39 | public boolean isCancelled() { 40 | return false; 41 | } 42 | }); 43 | if (!res.getFailures().isEmpty()) { 44 | StringBuilder sb = new StringBuilder(); 45 | sb.append("Failed decompilation of " + res.getFailures().size() + " classes: "); 46 | Iterator failureIterator = res.getFailures().iterator(); 47 | while (failureIterator.hasNext()) { 48 | DecompilationFailure dex = (DecompilationFailure)failureIterator.next(); 49 | sb.append(System.lineSeparator() + " ").append(dex.getMessage()); 50 | } 51 | System.out.println(sb.toString()); 52 | } 53 | System.out.println("Compilation results: " + res.getDecompiledFiles().size() + " succeeded, " + res.getFailures().size() + " failed."); 54 | decompiler.close(); 55 | Long end = System.currentTimeMillis(); 56 | return end - start; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /java-decompiler/src/main/java/com/wdbyte/decompiler/HardCode.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.decompiler; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.stream.IntStream; 6 | 7 | import org.benf.cfr.reader.util.functors.UnaryFunction; 8 | 9 | /** 10 | * @author https://github.com/niumoo 11 | * @date 2021/05/16 12 | */ 13 | public class HardCode { 14 | public HardCode(A a, B b) { } 15 | 16 | public static void test(int... args) { } 17 | 18 | public static void main(String... args) { 19 | test(1, 2, 3, 4, 5, 6); 20 | } 21 | 22 | int byteAnd0() { 23 | int b = 1; 24 | int x = 0; 25 | do { 26 | b = (byte)((b ^ x)); 27 | } while (b++ < 10); 28 | return b; 29 | } 30 | 31 | private void a(Integer i) { 32 | a(i); 33 | b(i); 34 | c(i); 35 | } 36 | 37 | private void b(int i) { 38 | a(i); 39 | b(i); 40 | c(i); 41 | } 42 | 43 | private void c(double d) { 44 | c(d); 45 | d(d); 46 | } 47 | 48 | private void d(Double d) { 49 | c(d); 50 | d(d); 51 | } 52 | 53 | private void e(Short s) { 54 | b(s); 55 | c(s); 56 | e(s); 57 | f(s); 58 | } 59 | 60 | private void f(short s) { 61 | b(s); 62 | c(s); 63 | e(s); 64 | f(s); 65 | } 66 | 67 | void test1(String path) { 68 | try { 69 | int x = 3; 70 | } catch (NullPointerException t) { 71 | System.out.println("File Not found"); 72 | if (path == null) { return; } 73 | throw t; 74 | } finally { 75 | System.out.println("Fred"); 76 | if (path == null) { throw new IllegalStateException(); } 77 | } 78 | } 79 | 80 | private final List stuff = new ArrayList<>();{ 81 | stuff.add(1); 82 | stuff.add(2); 83 | } 84 | 85 | public static int plus(boolean t, int a, int b) { 86 | int c = t ? a : b; 87 | return c; 88 | } 89 | 90 | // Lambda 91 | Integer lambdaInvoker(int arg, UnaryFunction fn) { 92 | return fn.invoke(arg); 93 | } 94 | 95 | // Lambda 96 | public int testLambda() { 97 | return lambdaInvoker(3, x -> x + 1); 98 | // return 1; 99 | } 100 | 101 | // Lambda 102 | public Integer testLambda(List stuff, int y, boolean b) { 103 | return stuff.stream().filter(b ? x -> x > y : x -> x < 3).findFirst().orElse(null); 104 | } 105 | 106 | // stream 107 | public static void testStream(List list) { 108 | IntStream s = list.stream() 109 | .filter(x -> { 110 | System.out.println(x); 111 | return x.intValue() / 2 == 0; 112 | }) 113 | .map(x -> (Integer)x+2) 114 | .mapToInt(x -> x); 115 | s.toArray(); 116 | } 117 | 118 | // switch 119 | public void testSwitch1(){ 120 | int i = 0; 121 | switch(((Long)(i + 1L)) + "") { 122 | case "1": 123 | System.out.println("one"); 124 | } 125 | } 126 | // switch 127 | public void testSwitch2(String string){ 128 | switch (string) { 129 | case "apples": 130 | System.out.println("apples"); 131 | break; 132 | case "pears": 133 | System.out.println("pears"); 134 | break; 135 | } 136 | } 137 | 138 | // switch 139 | public static void testSwitch3(int x) { 140 | while (true) { 141 | if (x < 5) { 142 | switch ("test") { 143 | case "okay": 144 | continue; 145 | default: 146 | continue; 147 | } 148 | } 149 | System.out.println("wow x2!"); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /java-decompiler/src/main/java/com/wdbyte/decompiler/JDCoreTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.decompiler; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.Enumeration; 10 | import java.util.HashMap; 11 | import java.util.jar.JarFile; 12 | import java.util.zip.ZipEntry; 13 | import java.util.zip.ZipFile; 14 | 15 | import org.apache.commons.io.IOUtils; 16 | import org.apache.commons.lang3.StringUtils; 17 | import org.jd.core.v1.ClassFileToJavaSourceDecompiler; 18 | import org.jd.core.v1.api.loader.Loader; 19 | import org.jd.core.v1.api.printer.Printer; 20 | 21 | /** 22 | * @author https://github.com/niumoo 23 | * @date 2021/05/15 24 | */ 25 | public class JDCoreTest { 26 | 27 | public static void main(String[] args) throws Exception { 28 | JDCoreDecompiler jdCoreDecompiler = new JDCoreDecompiler(); 29 | Long time = jdCoreDecompiler.decompiler("decompiler.jar","jd_output_jar"); 30 | System.out.println(String.format("decompiler time: %dms", time)); 31 | } 32 | } 33 | 34 | 35 | class JDCoreDecompiler{ 36 | 37 | private ClassFileToJavaSourceDecompiler decompiler = new ClassFileToJavaSourceDecompiler(); 38 | // 存放字节码 39 | private HashMap classByteMap = new HashMap<>(); 40 | 41 | /** 42 | * 注意:没有考虑一个 Java 类编译出多个 Class 文件的情况。 43 | * 44 | * @param source 45 | * @param target 46 | * @return 47 | * @throws Exception 48 | */ 49 | public Long decompiler(String source,String target) throws Exception { 50 | long start = System.currentTimeMillis(); 51 | // 解压 52 | archive(source); 53 | for (String className : classByteMap.keySet()) { 54 | String path = StringUtils.substringBeforeLast(className, "/"); 55 | String name = StringUtils.substringAfterLast(className, "/"); 56 | if (StringUtils.contains(name, "$")) { 57 | name = StringUtils.substringAfterLast(name, "$"); 58 | } 59 | name = StringUtils.replace(name, ".class", ".java"); 60 | decompiler.decompile(loader, printer, className); 61 | String context = printer.toString(); 62 | Path targetPath = Paths.get(target + "/" + path + "/" + name); 63 | if (!Files.exists(Paths.get(target + "/" + path))) { 64 | Files.createDirectories(Paths.get(target + "/" + path)); 65 | } 66 | Files.deleteIfExists(targetPath); 67 | Files.createFile(targetPath); 68 | Files.write(targetPath, context.getBytes()); 69 | } 70 | return System.currentTimeMillis() - start; 71 | } 72 | private void archive(String path) throws IOException { 73 | try (ZipFile archive = new JarFile(new File(path))) { 74 | Enumeration entries = archive.entries(); 75 | while (entries.hasMoreElements()) { 76 | ZipEntry entry = entries.nextElement(); 77 | if (!entry.isDirectory()) { 78 | String name = entry.getName(); 79 | if (name.endsWith(".class")) { 80 | byte[] bytes = null; 81 | try (InputStream stream = archive.getInputStream(entry)) { 82 | bytes = IOUtils.toByteArray(stream); 83 | } 84 | classByteMap.put(name, bytes); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | private Loader loader = new Loader() { 92 | @Override 93 | public byte[] load(String internalName) { 94 | return classByteMap.get(internalName); 95 | } 96 | @Override 97 | public boolean canLoad(String internalName) { 98 | return classByteMap.containsKey(internalName); 99 | } 100 | }; 101 | 102 | private Printer printer = new Printer() { 103 | protected static final String TAB = " "; 104 | protected static final String NEWLINE = "\n"; 105 | protected int indentationCount = 0; 106 | protected StringBuilder sb = new StringBuilder(); 107 | @Override public String toString() { 108 | String toString = sb.toString(); 109 | sb = new StringBuilder(); 110 | return toString; 111 | } 112 | @Override public void start(int maxLineNumber, int majorVersion, int minorVersion) {} 113 | @Override public void end() {} 114 | @Override public void printText(String text) { sb.append(text); } 115 | @Override public void printNumericConstant(String constant) { sb.append(constant); } 116 | @Override public void printStringConstant(String constant, String ownerInternalName) { sb.append(constant); } 117 | @Override public void printKeyword(String keyword) { sb.append(keyword); } 118 | @Override public void printDeclaration(int type, String internalTypeName, String name, String descriptor) { sb.append(name); } 119 | @Override public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) { sb.append(name); } 120 | @Override public void indent() { this.indentationCount++; } 121 | @Override public void unindent() { this.indentationCount--; } 122 | @Override public void startLine(int lineNumber) { for (int i=0; i 0) sb.append(NEWLINE); } 125 | @Override public void startMarker(int type) {} 126 | @Override public void endMarker(int type) {} 127 | }; 128 | } 129 | -------------------------------------------------------------------------------- /java-decompiler/src/main/java/com/wdbyte/decompiler/JMHTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.decompiler; 2 | 3 | import java.io.IOException; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import org.openjdk.jmh.annotations.Benchmark; 7 | import org.openjdk.jmh.annotations.BenchmarkMode; 8 | import org.openjdk.jmh.annotations.Fork; 9 | import org.openjdk.jmh.annotations.Measurement; 10 | import org.openjdk.jmh.annotations.Mode; 11 | import org.openjdk.jmh.annotations.OutputTimeUnit; 12 | import org.openjdk.jmh.annotations.Scope; 13 | import org.openjdk.jmh.annotations.State; 14 | import org.openjdk.jmh.annotations.Warmup; 15 | import org.openjdk.jmh.runner.Runner; 16 | import org.openjdk.jmh.runner.RunnerException; 17 | import org.openjdk.jmh.runner.options.Options; 18 | import org.openjdk.jmh.runner.options.OptionsBuilder; 19 | 20 | /** 21 | * 微基准测试。 22 | * @author https://github.com/niumoo 23 | * @date 2021/05/15 24 | */ 25 | @BenchmarkMode(Mode.AverageTime) 26 | @State(Scope.Thread) 27 | @Fork(1) 28 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 29 | @Warmup(iterations = 3) 30 | @Measurement(iterations = 5) 31 | public class JMHTest { 32 | public static void main(String[] args) throws RunnerException { 33 | Options opt = new OptionsBuilder() 34 | .include(JMHTest.class.getSimpleName()) 35 | .build(); 36 | new Runner(opt).run(); 37 | } 38 | 39 | @Benchmark 40 | public Long cfr() throws IOException { 41 | return CFRTest.cfr("python2java4common-1.0.0-20180706.084921-1.jar", "./output_cfr"); 42 | } 43 | 44 | @Benchmark 45 | public Long procyon() throws IOException { 46 | return ProcyonTest.procyon("python2java4common-1.0.0-20180706.084921-1.jar", "./output_procyon"); 47 | } 48 | 49 | @Benchmark 50 | public Long fernflower() throws IOException { 51 | return FernflowerTest.fernflower("python2java4common-1.0.0-20180706.084921-1.jar", "./output_fernflower"); 52 | } 53 | 54 | @Benchmark 55 | public Long jdcore() throws Exception { 56 | JDCoreDecompiler jdCoreDecompiler = new JDCoreDecompiler(); 57 | return jdCoreDecompiler.decompiler("python2java4common-1.0.0-20180706.084921-1.jar", "./output_jdcore"); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /java-decompiler/src/main/java/com/wdbyte/decompiler/ProcyonTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.decompiler; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Path; 5 | import java.nio.file.Paths; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | import org.jboss.windup.decompiler.api.DecompilationFailure; 10 | import org.jboss.windup.decompiler.api.DecompilationListener; 11 | import org.jboss.windup.decompiler.api.DecompilationResult; 12 | import org.jboss.windup.decompiler.api.Decompiler; 13 | import org.jboss.windup.decompiler.procyon.ProcyonDecompiler; 14 | 15 | /** 16 | * Procyon 反编译测试 17 | * 18 | * @author https://github.com/niumoo 19 | * @date 2021/05/15 20 | */ 21 | public class ProcyonTest { 22 | public static void main(String[] args) throws IOException { 23 | Long time = procyon("decompiler.jar", "procyon_output_jar"); 24 | System.out.println(String.format("decompiler time: %dms", time)); 25 | } 26 | public static Long procyon(String source,String targetPath) throws IOException { 27 | long start = System.currentTimeMillis(); 28 | Path outDir = Paths.get(targetPath); 29 | Path archive = Paths.get(source); 30 | Decompiler dec = new ProcyonDecompiler(); 31 | DecompilationResult res = dec.decompileArchive(archive, outDir, new DecompilationListener() { 32 | public void decompilationProcessComplete() { 33 | System.out.println("decompilationProcessComplete"); 34 | } 35 | public void decompilationFailed(List inputPath, String message) { 36 | System.out.println("decompilationFailed"); 37 | } 38 | public void fileDecompiled(List inputPath, String outputPath) { 39 | } 40 | public boolean isCancelled() { 41 | return false; 42 | } 43 | }); 44 | 45 | if (!res.getFailures().isEmpty()) { 46 | StringBuilder sb = new StringBuilder(); 47 | sb.append("Failed decompilation of " + res.getFailures().size() + " classes: "); 48 | Iterator failureIterator = res.getFailures().iterator(); 49 | while (failureIterator.hasNext()) { 50 | DecompilationFailure dex = (DecompilationFailure)failureIterator.next(); 51 | sb.append(System.lineSeparator() + " ").append(dex.getMessage()); 52 | } 53 | System.out.println(sb.toString()); 54 | } 55 | System.out.println("Compilation results: " + res.getDecompiledFiles().size() + " succeeded, " + res.getFailures().size() + " failed."); 56 | dec.close(); 57 | Long end = System.currentTimeMillis(); 58 | return end - start; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /java-lab/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | lab-notes 7 | com.wdbyte 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | java-lab 13 | 14 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/classloader/BaseManager.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.classloader; 2 | 3 | /** 4 | *

5 | * 实现这个接口的子类,需要动态更新。也就是热加载 6 | * 7 | * @Author niujinpeng 8 | * @Date 2019/10/24 23:29 9 | */ 10 | public interface BaseManager { 11 | 12 | public void logic(); 13 | } 14 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/classloader/ClassLoadTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.classloader; 2 | 3 | /** 4 | *

5 | * 6 | * 测试 Java 类的热加载 7 | * 8 | * @Author niujinpeng 9 | * @Date 2019/10/24 23:55 10 | */ 11 | public class ClassLoadTest { 12 | 13 | public static void main(String[] args) { 14 | new Thread(new MsgHandle()).start(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/classloader/LoadInfo.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.classloader; 2 | 3 | /** 4 | *

5 | * 封装加载类的信息 6 | * 7 | * @Author niujinpeng 8 | * @Date 2019/10/24 23:32 9 | */ 10 | public class LoadInfo { 11 | 12 | /** 自定义的类加载器 */ 13 | private MyClasslLoader myClasslLoader; 14 | 15 | /** 记录要加载的类的时间戳-->加载的时间 */ 16 | private long loadTime; 17 | 18 | /** 需要被热加载的类 */ 19 | private BaseManager manager; 20 | 21 | public LoadInfo(MyClasslLoader myClasslLoader, long loadTime) { 22 | this.myClasslLoader = myClasslLoader; 23 | this.loadTime = loadTime; 24 | } 25 | 26 | public MyClasslLoader getMyClasslLoader() { 27 | return myClasslLoader; 28 | } 29 | 30 | public void setMyClasslLoader(MyClasslLoader myClasslLoader) { 31 | this.myClasslLoader = myClasslLoader; 32 | } 33 | 34 | public long getLoadTime() { 35 | return loadTime; 36 | } 37 | 38 | public void setLoadTime(long loadTime) { 39 | this.loadTime = loadTime; 40 | } 41 | 42 | public BaseManager getManager() { 43 | return manager; 44 | } 45 | 46 | public void setManager(BaseManager manager) { 47 | this.manager = manager; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/classloader/ManagerFactory.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.classloader; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | *

10 | * 加载 manager 的工厂 11 | * 12 | * @Author niujinpeng 13 | * @Date 2019/10/24 23:38 14 | */ 15 | public class ManagerFactory { 16 | 17 | /** 记录热加载类的加载信息 */ 18 | private static final Map loadTimeMap = new HashMap<>(); 19 | 20 | /** 要加载的类的 classpath */ 21 | public static final String CLASS_PATH = "D:\\IdeaProjectMy\\lab-notes\\target\\classes\\"; 22 | 23 | /** 实现热加载的类的全名称(包名+类名 ) */ 24 | public static final String MY_MANAGER = "net.codingme.box.classloader.MyManager"; 25 | 26 | public static BaseManager getManager(String className) { 27 | File loadFile = new File(CLASS_PATH + className.replaceAll("\\.", "/") + ".class"); 28 | // 获取最后一次修改时间 29 | long lastModified = loadFile.lastModified(); 30 | System.out.println("当前的类时间:" + lastModified); 31 | // loadTimeMap 不包含 ClassName 为 key 的信息,证明这个类没有被加载,要加载到 JVM 32 | if (loadTimeMap.get(className) == null) { 33 | load(className, lastModified); 34 | } // 加载类的时间戳变化了,我们同样要重新加载这个类到 JVM。 35 | else if (loadTimeMap.get(className).getLoadTime() != lastModified) { 36 | load(className, lastModified); 37 | } 38 | return loadTimeMap.get(className).getManager(); 39 | } 40 | 41 | /** 42 | * 加载 class ,缓存到 loadTimeMap 43 | * 44 | * @param className 45 | * @param lastModified 46 | */ 47 | private static void load(String className, long lastModified) { 48 | MyClasslLoader myClasslLoader = new MyClasslLoader(className); 49 | Class loadClass = null; 50 | // 加载 51 | try { 52 | loadClass = myClasslLoader.loadClass(className); 53 | } catch (ClassNotFoundException e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | BaseManager manager = newInstance(loadClass); 58 | LoadInfo loadInfo = new LoadInfo(myClasslLoader, lastModified); 59 | loadInfo.setManager(manager); 60 | loadTimeMap.put(className, loadInfo); 61 | } 62 | 63 | /** 64 | * 以反射的方式创建 BaseManager 的子类对象 65 | * 66 | * @param loadClass 67 | * @return 68 | */ 69 | private static BaseManager newInstance(Class loadClass) { 70 | try { 71 | return (BaseManager)loadClass.getConstructor(new Class[] {}).newInstance(new Object[] {}); 72 | } catch (InstantiationException e) { 73 | e.printStackTrace(); 74 | } catch (IllegalAccessException e) { 75 | e.printStackTrace(); 76 | } catch (InvocationTargetException e) { 77 | e.printStackTrace(); 78 | } catch (NoSuchMethodException e) { 79 | e.printStackTrace(); 80 | } 81 | return null; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/classloader/MsgHandle.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.classloader; 2 | 3 | /** 4 | *

5 | * 6 | * 后台启动一条线程,不断检测是否要刷新重新加载,实现了热加载的类 7 | * 8 | * @Author niujinpeng 9 | * @Date 2019/10/24 23:53 10 | */ 11 | public class MsgHandle implements Runnable { 12 | @Override 13 | public void run() { 14 | while (true) { 15 | BaseManager manager = ManagerFactory.getManager(ManagerFactory.MY_MANAGER); 16 | manager.logic(); 17 | try { 18 | Thread.sleep(2000); 19 | } catch (InterruptedException e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/classloader/MyClasslLoader.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.classloader; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | 7 | /** 8 | *

9 | * 自定义 Java类加载器来实现Java 类的热加载 10 | * 11 | * @Author niujinpeng 12 | * @Date 2019/10/24 23:22 13 | */ 14 | public class MyClasslLoader extends ClassLoader { 15 | 16 | /** 要加载的 Java 类的 classpath 路径 */ 17 | private String classpath; 18 | 19 | public MyClasslLoader(String classpath) { 20 | // 指定父加载器 21 | super(ClassLoader.getSystemClassLoader()); 22 | this.classpath = classpath; 23 | } 24 | 25 | @Override 26 | protected Class findClass(String name) throws ClassNotFoundException { 27 | byte[] data = this.loadClassData(name); 28 | return this.defineClass(name, data, 0, data.length); 29 | } 30 | 31 | /** 32 | * 加载 class 文件中的内容 33 | * 34 | * @param name 35 | * @return 36 | */ 37 | private byte[] loadClassData(String name) { 38 | try { 39 | // 传进来是带包名的 40 | name = name.replace(".", "//"); 41 | FileInputStream inputStream = new FileInputStream(new File(classpath + name + ".class")); 42 | // 定义字节数组输出流 43 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 44 | int b = 0; 45 | while ((b = inputStream.read()) != -1) { 46 | baos.write(b); 47 | } 48 | inputStream.close(); 49 | return baos.toByteArray(); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | return null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/classloader/MyManager.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.classloader; 2 | 3 | import java.time.LocalTime; 4 | 5 | /** 6 | *

7 | * BaseManager 这个接口的子类要实现类的热加载功能。 8 | * 9 | * @Author niujinpeng 10 | * @Date 2019/10/24 23:30 11 | */ 12 | public class MyManager implements BaseManager { 13 | 14 | @Override 15 | public void logic() { 16 | System.out.println(LocalTime.now() + ": Java类的热加载 Oh~~~~ "); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/aio/AioSocketServer.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.aio; 2 | 3 | import java.io.IOException; 4 | import java.io.UnsupportedEncodingException; 5 | import java.net.InetSocketAddress; 6 | import java.nio.ByteBuffer; 7 | import java.nio.channels.AsynchronousServerSocketChannel; 8 | import java.nio.channels.AsynchronousSocketChannel; 9 | import java.nio.channels.CompletionHandler; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | /** 15 | * AIO异步通信 16 | *

17 | * Java 异步IO 18 | * 在java的N-IO框架中,需要使用一个重要的selector负责代替应用查询已注册的通道,用于IO事件轮训, 19 | * 管理通道集合等。 20 | * 21 | * A-IO中不是“轮训”方式,而是订阅-通知的方式,所以不需要`selector`了,channel通道可以直接注册 22 | * 监听到操作系统。 23 | * 24 | * @Author niujinpeng 25 | * @Date 2018/10/28 15:58 26 | */ 27 | public class AioSocketServer { 28 | 29 | 30 | private static Object waitObject = new Object(); 31 | 32 | public static void main(String[] args) throws IOException, InterruptedException { 33 | AsynchronousServerSocketChannel serverSocket = AsynchronousServerSocketChannel.open(); 34 | 35 | // 监听IP的83端口 36 | serverSocket.bind(new InetSocketAddress("0.0.0.0", 83)); 37 | // 注册监听 38 | serverSocket.accept(null, new ServerSocketChannelHandle(serverSocket)); 39 | 40 | synchronized (waitObject) { 41 | waitObject.wait(); 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * 用于响应ServerSocketChannel事件 48 | */ 49 | class ServerSocketChannelHandle implements CompletionHandler { 50 | 51 | private static final Logger logger = LoggerFactory.getLogger(ServerSocketChannelHandle.class); 52 | 53 | private AsynchronousServerSocketChannel serverSocketChannel; 54 | 55 | ServerSocketChannelHandle(AsynchronousServerSocketChannel serverSocketChannel) { 56 | this.serverSocketChannel = serverSocketChannel; 57 | } 58 | 59 | 60 | @Override 61 | public void completed(AsynchronousSocketChannel socketChannel, Void attachment) { 62 | logger.info("completed(AsynchronousSocketChannel,Void)"); 63 | // 注册监听 64 | this.serverSocketChannel.accept(attachment, this); 65 | 66 | // 注册Read事件 67 | ByteBuffer readBuffer = ByteBuffer.allocate(50); 68 | socketChannel.read(readBuffer, new StringBuffer(), new SocketChannelReadHandle(socketChannel, readBuffer)); 69 | } 70 | 71 | @Override 72 | public void failed(Throwable exc, Void attachment) { 73 | logger.info("failed(Throwable exc, Void attachment)"); 74 | } 75 | } 76 | 77 | /** 78 | * 负责对每一个SocketChannel的数据获取事件进行监听 79 | */ 80 | class SocketChannelReadHandle implements CompletionHandler { 81 | 82 | private static final Logger logger = LoggerFactory.getLogger(SocketChannelReadHandle.class); 83 | 84 | private AsynchronousSocketChannel socketChannel; 85 | 86 | private ByteBuffer byteBuffer; 87 | 88 | SocketChannelReadHandle(AsynchronousSocketChannel socketChannel, ByteBuffer byteBuffer) { 89 | this.socketChannel = socketChannel; 90 | this.byteBuffer = byteBuffer; 91 | 92 | } 93 | 94 | @Override 95 | public void completed(Integer result, StringBuffer historyContext) { 96 | // 如果条件成立,说明客户端主动终止了TCP套接字 97 | if (result == -1) { 98 | try { 99 | this.socketChannel.close(); 100 | } catch (IOException e) { 101 | logger.error(e.toString()); 102 | } 103 | return; 104 | } 105 | 106 | logger.info("completed(Integer result, StringBuffer historyContext)"); 107 | this.byteBuffer.flip(); 108 | // 如果接收用的byte长度不够,AIO会自动处理成多次通知 109 | byte[] contexts = new byte[1024]; 110 | this.byteBuffer.get(contexts, 0, result); 111 | this.byteBuffer.clear(); 112 | try { 113 | String nowContent = new String(contexts, 0, result, "UTF-8"); 114 | historyContext.append(nowContent); 115 | logger.info("现在接受到的数据:" + historyContext); 116 | } catch (UnsupportedEncodingException e) { 117 | logger.error(e.toString()); 118 | } 119 | 120 | // 是否有结束标记 121 | if (historyContext.indexOf("over") == -1) { 122 | return; 123 | } 124 | 125 | // 收到"over“开始处理数据 126 | logger.info("开始处理数据..."); 127 | // 清空 128 | historyContext = new StringBuffer(); 129 | 130 | // 继续监听 131 | this.socketChannel.read(this.byteBuffer, historyContext, this); 132 | 133 | 134 | } 135 | 136 | @Override 137 | public void failed(Throwable exc, StringBuffer attachment) { 138 | logger.info("客户端异常关闭,服务端关闭TCP通道"); 139 | try { 140 | this.socketChannel.close(); 141 | } catch (IOException e) { 142 | logger.info(e.toString()); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/aio/SocketClient.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.aio; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | 5 | /** 6 | *

7 | * BIO客户端 8 | * 模拟20个客户端并发请求,服务端则使用单线程。 9 | * 10 | * @Author niujinpeng 11 | * @Date 2018/10/15 10:50 12 | */ 13 | public class SocketClient { 14 | public static void main(String[] args) throws InterruptedException { 15 | // 客户端数量 16 | Integer clientNumber = 1; 17 | CountDownLatch countDownLatch = new CountDownLatch(clientNumber); 18 | 19 | // 分别启动20个客户端 20 | for (int index = 0; index < clientNumber; index++, countDownLatch.countDown()) { 21 | SocketClientRequestThread client = new SocketClientRequestThread(countDownLatch, index); 22 | new Thread(client).start(); 23 | } 24 | 25 | synchronized (SocketClient.class) { 26 | SocketClient.class.wait(); 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/aio/SocketClientRequestThread.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.aio; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | import java.util.concurrent.CountDownLatch; 8 | 9 | /** 10 | *

11 | * 网络客户端,用于模拟请求 12 | * 13 | * @Author niujinpeng 14 | * @Date 2018/10/15 10:53 15 | */ 16 | class SocketClientRequestThread implements Runnable { 17 | 18 | private CountDownLatch countDownLatch; 19 | 20 | /** 21 | * 线程的编号 22 | */ 23 | private Integer clientIndex; 24 | 25 | 26 | public SocketClientRequestThread(CountDownLatch countDownLatch, Integer clientIndex) { 27 | this.countDownLatch = countDownLatch; 28 | this.clientIndex = clientIndex; 29 | } 30 | 31 | @Override 32 | public void run() { 33 | Socket socket = null; 34 | OutputStream clientRequest = null; 35 | InputStream clientResponse = null; 36 | try { 37 | socket = new Socket("localhost", 83); 38 | clientRequest = socket.getOutputStream(); 39 | clientResponse = socket.getInputStream(); 40 | 41 | //等待,直到SocketClientDaemon完成所有线程的启动,然后所有线程一起发送请求 42 | this.countDownLatch.await(); 43 | 44 | // 发送请求信息 45 | clientRequest.write(("这是第" + this.clientIndex + "个客户端的请求").getBytes()); 46 | clientRequest.flush(); 47 | 48 | clientRequest.write("over".getBytes()); 49 | clientRequest.flush(); 50 | 51 | // 等待服务器返回消息 52 | System.out.println("第" + this.clientIndex + "个客户端请求发送完成,等待服务器响应"); 53 | //int maxLen = 1024; 54 | //byte[] contentBytes = new byte[maxLen]; 55 | //int realLen; 56 | //String message = ""; 57 | // 58 | //// 等待服务端返回,in和out不能cloese 59 | //while ((realLen = clientResponse.read(contentBytes, 0, maxLen)) != -1) { 60 | // message += new String(contentBytes, 0, realLen); 61 | //} 62 | //System.out.println("第" + this.clientIndex + "个客户端接受到来自服务器的消息:" + message); 63 | 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } catch (InterruptedException e) { 67 | e.printStackTrace(); 68 | } finally { 69 | try { 70 | if (clientRequest != null) { 71 | clientRequest.close(); 72 | } 73 | if (clientRequest != null) { 74 | clientResponse.close(); 75 | } 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/bio/SocketClient.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.bio; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | 5 | /** 6 | *

7 | * BIO客户端 8 | * 模拟20个客户端并发请求,服务端则使用单线程。 9 | * 10 | * @Author niujinpeng 11 | * @Date 2018/10/15 10:50 12 | */ 13 | public class SocketClient { 14 | public static void main(String[] args) throws InterruptedException { 15 | // 客户端数量 16 | Integer clientNumber = 20; 17 | CountDownLatch countDownLatch = new CountDownLatch(clientNumber); 18 | 19 | // 分别启动20个客户端 20 | for (int index = 0; index < clientNumber; index++, countDownLatch.countDown()) { 21 | SocketClientRequestThread client = new SocketClientRequestThread(countDownLatch, index); 22 | new Thread(client).start(); 23 | } 24 | 25 | synchronized (SocketClient.class) { 26 | SocketClient.class.wait(); 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/bio/SocketClientRequestThread.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.bio; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | import java.util.concurrent.CountDownLatch; 8 | 9 | /** 10 | *

11 | * 网络客户端,用于模拟请求 12 | * 13 | * @Author niujinpeng 14 | * @Date 2018/10/15 10:53 15 | */ 16 | class SocketClientRequestThread implements Runnable { 17 | 18 | private CountDownLatch countDownLatch; 19 | 20 | /** 21 | * 线程的编号 22 | */ 23 | private Integer clientIndex; 24 | 25 | 26 | public SocketClientRequestThread(CountDownLatch countDownLatch, Integer clientIndex) { 27 | this.countDownLatch = countDownLatch; 28 | this.clientIndex = clientIndex; 29 | } 30 | 31 | @Override 32 | public void run() { 33 | Socket socket = null; 34 | OutputStream clientRequest = null; 35 | InputStream clientResponse = null; 36 | try { 37 | socket = new Socket("localhost", 83); 38 | clientRequest = socket.getOutputStream(); 39 | clientResponse = socket.getInputStream(); 40 | 41 | //等待,直到SocketClientDaemon完成所有线程的启动,然后所有线程一起发送请求 42 | this.countDownLatch.await(); 43 | 44 | // 发送请求信息 45 | clientRequest.write(("这是第" + this.clientIndex + "个客户端的请求").getBytes()); 46 | clientRequest.flush(); 47 | 48 | // 等待服务器返回消息 49 | System.out.println("第" + this.clientIndex + "个客户端请求发送完成,等待服务器响应"); 50 | int maxLen = 1024; 51 | byte[] contentBytes = new byte[maxLen]; 52 | int realLen; 53 | String message = ""; 54 | 55 | // 等待服务端返回,in和out不能cloese 56 | while ((realLen = clientResponse.read(contentBytes, 0, maxLen)) != -1) { 57 | message += new String(contentBytes, 0, realLen); 58 | } 59 | System.out.println("第" + this.clientIndex + "个客户端接受到来自服务器的消息:" + message); 60 | 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } finally { 66 | try { 67 | if (clientRequest != null) { 68 | clientRequest.close(); 69 | } 70 | if (clientRequest != null) { 71 | clientResponse.close(); 72 | } 73 | } catch (IOException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/bio/SocketServer.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.bio; 2 | 3 | 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | 10 | /** 11 | * BIO服务端 12 | *

13 | * 单线程阻塞的服务器端 14 | * 15 | * @Author niujinpeng 16 | * @Date 2018/10/15 11:17 17 | */ 18 | public class SocketServer { 19 | 20 | public static void main(String[] args) throws IOException { 21 | ServerSocket serverSocket = new ServerSocket(83); 22 | try { 23 | while (true) { 24 | // 阻塞,直到有数据准备完毕 25 | Socket socket = serverSocket.accept(); 26 | 27 | // 开始收取信息 28 | InputStream input = socket.getInputStream(); 29 | OutputStream output = socket.getOutputStream(); 30 | Integer sourcePort = socket.getPort(); 31 | int maxLen = 1024 * 2; 32 | byte[] contextBytes = new byte[maxLen]; 33 | 34 | // 阻塞,直到有数据准备完毕 35 | int realLen = input.read(contextBytes, 0, maxLen); 36 | // 读取信息 37 | String message = new String(contextBytes, 0, realLen); 38 | 39 | // 输出接收信息 40 | System.out.println("服务器收到来自端口【" + sourcePort + "】的信息:" + message); 41 | // 响应信息 42 | output.write("Done!".getBytes()); 43 | 44 | // 关闭 45 | output.close(); 46 | input.close(); 47 | socket.close(); 48 | 49 | } 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } finally { 53 | if (serverSocket != null) { 54 | serverSocket.close(); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/bio/SocketServerThread.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.bio; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.ServerSocket; 7 | import java.net.Socket; 8 | 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | /** 12 | * BIO服务端 13 | *

14 | * 多线程的阻塞的服务端 15 | *

16 | * 当然,接收到客户端的socket后,业务的处理过程可以交给一个线程来做。 17 | * 但还是改变不了socket被一个一个的做accept()的情况。 18 | * 19 | * @Author niujinpeng 20 | * @Date 2018/10/15 11:17 21 | */ 22 | @Slf4j 23 | public class SocketServerThread implements Runnable { 24 | 25 | private Socket socket; 26 | 27 | public SocketServerThread(Socket socket) { 28 | this.socket = socket; 29 | 30 | } 31 | 32 | public static void main(String[] args) throws Exception { 33 | ServerSocket serverSocket = new ServerSocket(83); 34 | try { 35 | while (true) { 36 | Socket socket = serverSocket.accept(); 37 | //当然业务处理过程可以交给一个线程(这里可以使用线程池),并且线程的创建是很耗资源的。 38 | //最终改变不了.accept()只能一个一个接受socket的情况,并且被阻塞的情况 39 | SocketServerThread socketServerThread = new SocketServerThread(socket); 40 | new Thread(socketServerThread).start(); 41 | } 42 | } catch (Exception e) { 43 | System.out.println(e.getMessage()); 44 | } finally { 45 | if (serverSocket != null) { 46 | serverSocket.close(); 47 | } 48 | } 49 | } 50 | 51 | 52 | @Override 53 | public void run() { 54 | InputStream in = null; 55 | OutputStream out = null; 56 | try { 57 | //下面我们收取信息 58 | in = socket.getInputStream(); 59 | out = socket.getOutputStream(); 60 | Integer sourcePort = socket.getPort(); 61 | int maxLen = 1024; 62 | byte[] contextBytes = new byte[maxLen]; 63 | //使用线程,同样无法解决read方法的阻塞问题, 64 | //也就是说read方法处同样会被阻塞,直到操作系统有数据准备好 65 | int realLen = in.read(contextBytes, 0, maxLen); 66 | //读取信息 67 | String message = new String(contextBytes, 0, realLen); 68 | 69 | //下面打印信息 70 | log.info("服务器收到来自于端口:" + sourcePort + "的信息:" + message); 71 | 72 | //下面开始发送信息 73 | out.write("回发响应信息!".getBytes()); 74 | } catch (Exception e) { 75 | log.error(e.getMessage(), e); 76 | } finally { 77 | //试图关闭 78 | try { 79 | if (in != null) { 80 | in.close(); 81 | } 82 | if (out != null) { 83 | out.close(); 84 | } 85 | if (this.socket != null) { 86 | this.socket.close(); 87 | } 88 | } catch (IOException e) { 89 | log.error(e.getMessage(), e); 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/jdknio/NioBuffer.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.jdknio; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.nio.ByteBuffer; 5 | 6 | /** 7 | * NIO-Buffer 8 | *

9 | * Buffer本质是可以写入可以读取的内存,这块内存被包装成了NIO的Buffer对象, 10 | * 然后为它提供一组用于访问的方法。Java则为java.nio.Buffer实现了基本数据 11 | * 类型的Buffer 12 | * 13 | * @Author niujinpeng 14 | * @Date 2018/10/24 18:28 15 | */ 16 | public class NioBuffer { 17 | 18 | public static void main(String[] args) throws UnsupportedEncodingException { 19 | // 申请一个大小为1024bytes的缓冲buffer 20 | ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 21 | System.out.println("申请到的Buffer:" + byteBuffer); 22 | 23 | // 写入helloworld到buffer 24 | byteBuffer.put("HelloWorld".getBytes()); 25 | System.out.println("写入HelloWorld到Buffer:" + byteBuffer); 26 | 27 | // 切换为读模式 28 | byteBuffer.flip(); 29 | // 当前Buffer已存放的大小 30 | int length = byteBuffer.remaining(); 31 | byte[] bytes = new byte[length]; 32 | 33 | // 读取bytes长度的数据 34 | byteBuffer.get(bytes); 35 | System.out.println("从buffer读取到数据:" + new String(bytes, "UTF-8")); 36 | 37 | // 切换为compact 清空已读取的数据 38 | byteBuffer.compact(); 39 | System.out.println("读取后的Buffer:" + byteBuffer); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/jdknio/NioChannel.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.jdknio; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.FileChannel; 7 | 8 | /** 9 | * NIO-Channel 10 | *

11 | * 通道Channel和流类似,不同的是通道的工作模式可以是全双工。 12 | * 也就是说既可以读取,也可以写入。同时也可以异步的进行读写。 13 | * `Channel`连接着底层数据与缓冲区`Buffer`。 14 | * 15 | * @Author niujinpeng 16 | * @Date 2018/10/25 16:27 17 | */ 18 | public class NioChannel { 19 | 20 | public static void main(String[] args) throws IOException { 21 | // 申请一个大小为1024bytes的缓冲buffer 22 | ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 23 | 24 | // 初始化Channel数据 25 | FileInputStream fis = new FileInputStream("f:/test.txt"); 26 | FileChannel channel = fis.getChannel(); 27 | System.out.println("Init Channel size:" + channel.size()); 28 | 29 | // 从channel中读取数据 30 | int read = channel.read(byteBuffer); 31 | System.out.println("Read Size :" + read); 32 | System.out.println("byteBuffer:"+byteBuffer); 33 | 34 | // 切换到读取模式 35 | byteBuffer.flip(); 36 | 37 | // 输出byteBuffer内容 38 | System.out.print("print byteBuffer:"); 39 | while (byteBuffer.hasRemaining()){ 40 | System.out.print((char) byteBuffer.get()); 41 | } 42 | 43 | byteBuffer.clear(); 44 | System.out.println(byteBuffer); 45 | fis.close(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/jdknio/NioSelector.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.jdknio; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.ServerSocket; 6 | import java.nio.ByteBuffer; 7 | import java.nio.channels.SelectableChannel; 8 | import java.nio.channels.SelectionKey; 9 | import java.nio.channels.Selector; 10 | import java.nio.channels.ServerSocketChannel; 11 | import java.nio.channels.SocketChannel; 12 | import java.util.Iterator; 13 | import java.util.Set; 14 | 15 | /** 16 | *

17 | * NIO-Selector 18 | * 选择器的使用测试 19 | * Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读 20 | * 写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接 21 | * 。我们也可以称Selector为轮询代理器,事件订阅器或者channel容器管理器。 22 | * 应用程序将向Selector对象注册需要它关注的Channel,以及具体的某一个Channel会对哪些 23 | * IO事件感兴趣。Selector中也会维护一个“已经注册的Channel”的容器。 24 | * 25 | * @Author niujinpeng 26 | * @Date 2018/10/26 15:31 27 | */ 28 | public class NioSelector { 29 | 30 | public static void main(String[] args) throws IOException { 31 | // 获取channel 32 | ServerSocketChannel channel = ServerSocketChannel.open(); 33 | // channel是否阻塞 34 | channel.configureBlocking(false); 35 | // 监听88端口 36 | ServerSocket socket = channel.socket(); 37 | socket.bind(new InetSocketAddress(83)); 38 | 39 | 40 | // 创建选择器Selector 41 | Selector selector = Selector.open(); 42 | // 像选择器中注册channel 43 | channel.register(selector, SelectionKey.OP_ACCEPT); 44 | 45 | while (true) { 46 | // 阻塞到有一个就绪 47 | int readyChannel = selector.select(); 48 | if (readyChannel == 0) { 49 | continue; 50 | } 51 | Set selectionKeys = selector.selectedKeys(); 52 | Iterator iterator = selectionKeys.iterator(); 53 | while (iterator.hasNext()) { 54 | SelectionKey selectionKey = iterator.next(); 55 | iterator.remove(); 56 | // 是否可以接受 57 | if (selectionKey.isAcceptable()) { 58 | System.out.println("准备就绪"); 59 | SelectableChannel selectableChannel = selectionKey.channel(); 60 | ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectableChannel; 61 | SocketChannel socketChannel = serverSocketChannel.accept(); 62 | socketChannel.configureBlocking(false); 63 | // 注册感兴趣事件-读取 64 | socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(2048)); 65 | } else if (selectionKey.isConnectable()) { 66 | System.out.println("已连接"); 67 | 68 | } else if (selectionKey.isReadable()) { 69 | System.out.println("可以读取"); 70 | 71 | } else if (selectionKey.isWritable()) { 72 | System.out.println("可以写入"); 73 | 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/jdknio/NioSocketServer.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.jdknio; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.ServerSocket; 6 | import java.net.URLDecoder; 7 | import java.net.URLEncoder; 8 | import java.nio.ByteBuffer; 9 | import java.nio.channels.SelectableChannel; 10 | import java.nio.channels.SelectionKey; 11 | import java.nio.channels.Selector; 12 | import java.nio.channels.ServerSocketChannel; 13 | import java.nio.channels.SocketChannel; 14 | import java.util.Iterator; 15 | 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | /** 20 | *

21 | * 使用Java NIO框架,实现一个支持多路复用IO的服务器端 22 | * 23 | * @Author niujinpeng 24 | * @Date 2018/10/16 0:53 25 | */ 26 | public class NioSocketServer { 27 | /** 28 | * 日志 29 | */ 30 | private static final Logger LOGGER = LoggerFactory.getLogger(NioSocketServer.class); 31 | 32 | public static void main(String[] args) throws IOException { 33 | ServerSocketChannel serverChannel = ServerSocketChannel.open(); 34 | // 是否阻塞 35 | serverChannel.configureBlocking(false); 36 | ServerSocket serverSocket = serverChannel.socket(); 37 | serverSocket.setReuseAddress(true); 38 | serverSocket.bind(new InetSocketAddress(83)); 39 | 40 | Selector selector = Selector.open(); 41 | // 服务器通道只能注册SelectionKey.OP_ACCEPT事件 42 | serverChannel.register(selector, SelectionKey.OP_ACCEPT); 43 | 44 | while (true) { 45 | // java程序对多路复用IO的支持也包括了阻塞模式 和非阻塞模式两种。 46 | if (selector.select(100) == 0) { 47 | //LOGGER.info("本次询问selector没有获取到任何准备好的事件"); 48 | continue; 49 | } 50 | 51 | // 询问系统,所有获取到的事件类型 52 | Iterator selectionKeys = selector.selectedKeys().iterator(); 53 | while (selectionKeys.hasNext()) { 54 | SelectionKey readKey = selectionKeys.next(); 55 | // 上面获取到的readKey要移除,不然会一直存在selector.selectedKeys()的集合之中 56 | selectionKeys.remove(); 57 | 58 | SelectableChannel selectableChannel = readKey.channel(); 59 | if (readKey.isValid() && readKey.isAcceptable()) { 60 | LOGGER.info("--------------channel通道已经准备完毕-------------"); 61 | /* 62 | * 当server socket channel通道已经准备好,就可以从server socket channel中获取socketchannel了 63 | * 拿到socket channel后,要做的事情就是马上到selector注册这个socket channel感兴趣的事情。 64 | * 否则无法监听到这个socket channel到达的数据 65 | * */ 66 | ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectableChannel; 67 | SocketChannel socketChannel = serverSocketChannel.accept(); 68 | registerSocketChannel(socketChannel, selector); 69 | } else if (readKey.isValid() && readKey.isConnectable()) { 70 | LOGGER.info("--------------socket channel 建立连接-------------"); 71 | } else if (readKey.isValid() && readKey.isReadable()) { 72 | LOGGER.info("--------------socket channel 数据准备完成,可以开始读取-------------"); 73 | try { 74 | readSocketChannel(readKey); 75 | } catch (Exception e) { 76 | LOGGER.error(e.getMessage()); 77 | } 78 | } 79 | } 80 | } 81 | 82 | } 83 | 84 | /** 85 | * 在server socket channel接收到/准备好 一个新的 TCP连接后。 86 | * 就会向程序返回一个新的socketChannel。
87 | * 但是这个新的socket channel并没有在selector“选择器/代理器”中注册, 88 | * 所以程序还没法通过selector通知这个socket channel的事件。 89 | * 于是我们拿到新的socket channel后,要做的第一个事情就是到selector“选择器/代理器”中注册这个 90 | * socket channel感兴趣的事件 91 | * 92 | * @param socketChannel 93 | * @param selector 94 | * @throws Exception 95 | */ 96 | private static void registerSocketChannel(SocketChannel socketChannel, Selector selector) { 97 | // 是否阻塞 98 | try { 99 | socketChannel.configureBlocking(false); 100 | // 读模式只能读,写模式可以同时读 101 | // socket通道可以且只可以注册三种事件SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT 102 | socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(2048)); 103 | } catch (IOException e) { 104 | LOGGER.info(e.toString(), e); 105 | } 106 | 107 | } 108 | 109 | private static void readSocketChannel(SelectionKey readKey) throws Exception { 110 | SocketChannel clientSocketChannel = (SocketChannel) readKey.channel(); 111 | //获取客户端使用的端口 112 | InetSocketAddress sourceSocketAddress = (InetSocketAddress) clientSocketChannel.getRemoteAddress(); 113 | int sourcePort = sourceSocketAddress.getPort(); 114 | 115 | // 拿到这个socket channel使用的缓存区,准备读取数据 116 | // 解缓存区的用法概念,实际上重要的就是三个元素capacity,position和limit。 117 | ByteBuffer contextBytes = (ByteBuffer) readKey.attachment(); 118 | // 通道的数据写入到【缓存区】 119 | // 由于之前设置了ByteBuffer的大小为2048 byte,所以可以存在写入不完的情况,需要调整 120 | int realLen = -1; 121 | try { 122 | realLen = clientSocketChannel.read(contextBytes); 123 | } catch (Exception e) { 124 | LOGGER.error(e.getMessage()); 125 | clientSocketChannel.close(); 126 | return; 127 | } 128 | 129 | // 如果缓存中没有数据 130 | if (realLen == -1) { 131 | LOGGER.warn("--------------缓存中没有数据-------------"); 132 | return; 133 | } 134 | 135 | // 将缓存区读写状态模式进行切换 136 | contextBytes.flip(); 137 | // 处理编码问题 138 | byte[] messageBytes = contextBytes.array(); 139 | String messageEncode = new String(messageBytes, "UTF-8"); 140 | String message = URLDecoder.decode(messageEncode, "UTF-8"); 141 | 142 | // 接受到了"over"则清空buffer,并响应,否则不清空缓存,并还原Buffer写状态 143 | if (message.indexOf("over") != -1) { 144 | //清空已经读取的缓存,并从新切换为写状态(这里要注意clear()和capacity()两个方法的区别) 145 | contextBytes.clear(); 146 | LOGGER.info("端口【" + sourcePort + "】客户端发来的信息:" + message); 147 | LOGGER.info("端口【" + sourcePort + "】客户端消息发送完毕"); 148 | // 响应 149 | ByteBuffer sendBuffer = ByteBuffer.wrap(URLEncoder.encode("Done!", "UTF-8").getBytes()); 150 | clientSocketChannel.write(sendBuffer); 151 | clientSocketChannel.close(); 152 | } else { 153 | LOGGER.info("端口【" + sourcePort + "】客户端发来的信息还未完毕,继续接收"); 154 | // limit和capacity的值一致,position的位置是realLen的位置 155 | contextBytes.position(realLen); 156 | contextBytes.limit(contextBytes.capacity()); 157 | } 158 | } 159 | 160 | 161 | } 162 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/nio/SocketServerNioListen.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.nio; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.ServerSocket; 7 | import java.net.Socket; 8 | import java.net.SocketException; 9 | import java.net.SocketTimeoutException; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | /** 15 | *

16 | * 非阻塞IO - 监听非阻塞 17 | * 通过设置超时时间实现 18 | * 19 | * @Author niujinpeng 20 | * @Date 2018/10/15 14:53 21 | */ 22 | public class SocketServerNioListen { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(SocketServerNioListen.class); 25 | 26 | private static Object xWait = new Object(); 27 | 28 | public static void main(String[] args) throws IOException { 29 | ServerSocket serverSocket = null; 30 | 31 | try { 32 | serverSocket = new ServerSocket(83); 33 | serverSocket.setSoTimeout(100); 34 | while (true) { 35 | Socket socket = null; 36 | try { 37 | socket = serverSocket.accept(); 38 | } catch (SocketTimeoutException e) { 39 | synchronized (SocketServerNioListen.xWait) { 40 | logger.info("没有从底层接收到任务数据报文,等待10ms"); 41 | SocketServerNioListen.xWait.wait(10); 42 | } 43 | continue; 44 | } 45 | 46 | InputStream input = socket.getInputStream(); 47 | OutputStream output = socket.getOutputStream(); 48 | Integer sourcePort = socket.getPort(); 49 | int maxLen = 2048; 50 | byte[] contentBytes = new byte[maxLen]; 51 | int realLen; 52 | StringBuffer message = new StringBuffer(); 53 | // 读取的时候,程序会阻塞,知道系统把网络传过来的数据准备完毕 54 | while ((realLen = input.read(contentBytes, 0, maxLen)) != -1) { 55 | message.append(new String(contentBytes, 0, realLen)); 56 | /** 57 | * 如果收到over,表示传送完毕 58 | */ 59 | if (message.toString().endsWith("over")) { 60 | break; 61 | } 62 | } 63 | 64 | // 输出信息 65 | logger.info("服务器收到来自端口" + sourcePort + "的消息:" + message.toString()); 66 | // 响应 67 | output.write("Done!".getBytes()); 68 | 69 | output.close(); 70 | input.close(); 71 | socket.close(); 72 | } 73 | } catch (SocketException | InterruptedException e) { 74 | logger.error(e.getMessage(), e); 75 | } finally { 76 | if (serverSocket != null) { 77 | serverSocket.close(); 78 | } 79 | } 80 | } 81 | 82 | } 83 | 84 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/nio/SocketServerNioListenAndRead.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.nio; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.ServerSocket; 7 | import java.net.Socket; 8 | import java.net.SocketException; 9 | import java.net.SocketTimeoutException; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | /** 15 | *

16 | * 非阻塞IO - 监听非阻塞 - 读取非阻塞 17 | * 18 | * @Author niujinpeng 19 | * @Date 2018/10/15 14:53 20 | */ 21 | public class SocketServerNioListenAndRead { 22 | /** 23 | * 日志 24 | */ 25 | private static final Logger logger = LoggerFactory.getLogger(SocketServerNioListenAndRead.class); 26 | private static Object xWait = new Object(); 27 | 28 | public static void main(String[] args) throws IOException { 29 | ServerSocket serverSocket = null; 30 | 31 | try { 32 | serverSocket = new ServerSocket(83); 33 | serverSocket.setSoTimeout(100); 34 | while (true) { 35 | Socket socket = null; 36 | try { 37 | socket = serverSocket.accept(); 38 | } catch (SocketTimeoutException e) { 39 | synchronized (SocketServerNioListenAndRead.xWait) { 40 | logger.info("没有从底层接收到任务数据报文,等待10ms,,模拟事件X的处理时间"); 41 | SocketServerNioListenAndRead.xWait.wait(10); 42 | } 43 | continue; 44 | } 45 | 46 | InputStream input = socket.getInputStream(); 47 | OutputStream output = socket.getOutputStream(); 48 | Integer sourcePort = socket.getPort(); 49 | int maxLen = 2048; 50 | byte[] contentBytes = new byte[maxLen]; 51 | int realLen; 52 | StringBuffer message = new StringBuffer(); 53 | 54 | // 接收消息非阻塞实现 55 | socket.setSoTimeout(10); 56 | 57 | BIORead: 58 | while (true) { 59 | try { 60 | // 读取的时候,程序会阻塞,知道系统把网络传过来的数据准备完毕 61 | while ((realLen = input.read(contentBytes, 0, maxLen)) != -1) { 62 | message.append(new String(contentBytes, 0, realLen)); 63 | /** 64 | * 如果收到over,表示传送完毕 65 | */ 66 | if (message.toString().endsWith("over")) { 67 | break BIORead; 68 | } 69 | } 70 | } catch (SocketTimeoutException e) { 71 | //=========================================================== 72 | // 执行到这里,说明本次read没有接收到任何数据流 73 | // 主线程在这里又可以做一些事情,记为Y 74 | //=========================================================== 75 | logger.info("这次没有从底层接收到任务数据报文,等待10毫秒,模拟事件Y的处理时间"); 76 | continue; 77 | } 78 | 79 | } 80 | 81 | // 输出信息 82 | logger.info("服务器收到来自端口" + sourcePort + "的消息:" + message.toString()); 83 | // 响应 84 | output.write("Done!".getBytes()); 85 | 86 | output.close(); 87 | input.close(); 88 | socket.close(); 89 | } 90 | } catch (SocketException | InterruptedException e) { 91 | logger.error(e.getMessage(), e); 92 | } finally { 93 | if (serverSocket != null) { 94 | serverSocket.close(); 95 | } 96 | } 97 | } 98 | 99 | } 100 | 101 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/io/nio/SocketServerNioListenThread.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.io.nio; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.net.SocketTimeoutException; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | *

14 | * 非阻塞IO - 监听非阻塞 - 读取非阻塞 15 | * 通过加入线程的概念,让socket server能够在应用层面 16 | * 通过非阻塞的方式同时处理多个socket套接字 17 | *

18 | * 此时可以实现非阻塞的IO,但是因为调用了系统底层的阻塞同步IO, 19 | * 因此仍然没有从根本上解决问题 20 | * 21 | * @Author niujinpeng 22 | * @Date 2018/10/15 15:23 23 | */ 24 | public class SocketServerNioListenThread { 25 | 26 | private static Object xWait = new Object(); 27 | 28 | private static final Logger LOGGER = LoggerFactory.getLogger(SocketServerNioListenThread.class); 29 | 30 | public static void main(String[] args) throws Exception { 31 | ServerSocket serverSocket = new ServerSocket(83); 32 | serverSocket.setSoTimeout(100); 33 | try { 34 | while (true) { 35 | Socket socket = null; 36 | try { 37 | socket = serverSocket.accept(); 38 | } catch (SocketTimeoutException e1) { 39 | //=========================================================== 40 | // 执行到这里,说明本次accept没有接收到任何TCP连接 41 | // 主线程在这里就可以做一些事情,记为X 42 | //=========================================================== 43 | synchronized (SocketServerNioListenThread.xWait) { 44 | LOGGER.info("这次没有从底层接收到任何TCP连接,等待10毫秒,模拟事件X的处理时间"); 45 | SocketServerNioListenThread.xWait.wait(10); 46 | } 47 | continue; 48 | } 49 | //当然业务处理过程可以交给一个线程(这里可以使用线程池),并且线程的创建是很耗资源的。 50 | //最终改变不了.accept()只能一个一个接受socket连接的情况 51 | SocketServerThread socketServerThread = new SocketServerThread(socket); 52 | new Thread(socketServerThread).start(); 53 | } 54 | } catch (Exception e) { 55 | LOGGER.error(e.getMessage(), e); 56 | } finally { 57 | if (serverSocket != null) { 58 | serverSocket.close(); 59 | } 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * 当然,接收到客户端的socket后,业务的处理过程可以交给一个线程来做。 66 | * 但还是改变不了socket被一个一个的做accept()的情况。 67 | * 68 | * @author niujinpeng 69 | */ 70 | class SocketServerThread implements Runnable { 71 | 72 | /** 73 | * 日志 74 | */ 75 | private static final Logger LOGGER = LoggerFactory.getLogger(SocketServerThread.class); 76 | 77 | private Socket socket; 78 | 79 | public SocketServerThread(Socket socket) { 80 | this.socket = socket; 81 | } 82 | 83 | @Override 84 | public void run() { 85 | InputStream in = null; 86 | OutputStream out = null; 87 | try { 88 | in = socket.getInputStream(); 89 | out = socket.getOutputStream(); 90 | Integer sourcePort = socket.getPort(); 91 | int maxLen = 2048; 92 | byte[] contextBytes = new byte[maxLen]; 93 | int realLen; 94 | StringBuffer message = new StringBuffer(); 95 | //下面我们收取信息(设置成非阻塞方式,这样read信息的时候,又可以做一些其他事情) 96 | this.socket.setSoTimeout(10); 97 | BIORead: 98 | while (true) { 99 | try { 100 | while ((realLen = in.read(contextBytes, 0, maxLen)) != -1) { 101 | message.append(new String(contextBytes, 0, realLen)); 102 | /* 103 | * 我们假设读取到“over”关键字, 104 | * 表示客户端的所有信息在经过若干次传送后,完成 105 | * */ 106 | if (message.indexOf("over") != -1) { 107 | break BIORead; 108 | } 109 | } 110 | } catch (SocketTimeoutException e2) { 111 | //=========================================================== 112 | // 执行到这里,说明本次read没有接收到任何数据流 113 | // 主线程在这里又可以做一些事情,记为Y 114 | //=========================================================== 115 | LOGGER.info("这次没有从底层接收到任务数据报文,等待10毫秒,模拟事件Y的处理时间"); 116 | continue; 117 | } 118 | } 119 | //下面打印信息 120 | Long threadId = Thread.currentThread().getId(); 121 | LOGGER.info("服务器(线程:" + threadId + ")收到来自于端口:" + sourcePort + "的信息:" + message); 122 | 123 | //下面开始发送信息 124 | out.write("回发响应信息!".getBytes()); 125 | 126 | //关闭 127 | out.close(); 128 | in.close(); 129 | this.socket.close(); 130 | } catch (Exception e) { 131 | LOGGER.error(e.getMessage(), e); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jdk/ModCountExceptionDemo.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jdk; 2 | 3 | import java.lang.Thread.State; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.concurrent.Phaser; 9 | 10 | import org.junit.Test; 11 | 12 | /** 13 | * 并发修改异常的绕过方式 14 | * 15 | * @author github.com/niumoo 16 | * @date 2021/02/11 17 | */ 18 | public class ModCountExceptionDemo { 19 | 20 | @Test 21 | public void updateCollections() { 22 | List list = new ArrayList<>(); 23 | Collections.addAll(list, "Hello", "World", "C++", "Java"); 24 | 25 | list.listIterator(); 26 | Iterator iterator = list.iterator(); 27 | System.out.println(iterator.next()); 28 | System.out.println(iterator.next()); 29 | list.remove("C++"); 30 | System.out.println(iterator.next()); 31 | } 32 | 33 | @Test 34 | public void updateCollectionsFor() { 35 | List list = new ArrayList<>(); 36 | Collections.addAll(list, "Hello", "World", "C++", "Java"); 37 | 38 | list.listIterator(); 39 | Iterator iterator = list.iterator(); 40 | System.out.println(iterator.next()); 41 | System.out.println(iterator.next()); 42 | list.remove("C++"); 43 | // 40 多亿次遍历,溢出到负数,继续溢出到原值 44 | for (int n = Integer.MIN_VALUE; n < Integer.MAX_VALUE; n++) { ((ArrayList)list).trimToSize(); } 45 | System.out.println(iterator.next()); 46 | } 47 | 48 | @Test 49 | public void updateCollectionThread() { 50 | List list = new ArrayList<>(); 51 | Collections.addAll(list, "Hello", "World", "C++", "Java"); 52 | 53 | list.listIterator(); 54 | Iterator iterator = list.iterator(); 55 | System.out.println(iterator.next()); 56 | System.out.println(iterator.next()); 57 | 58 | // 开始操作 59 | list.set(2, "Java"); 60 | Phaser phaser = new Phaser(2); 61 | Thread main = Thread.currentThread(); 62 | new Thread(() -> { 63 | synchronized (System.out) { 64 | phaser.arriveAndDeregister(); 65 | while (main.getState() != State.BLOCKED) { 66 | try { 67 | Thread.sleep(100); 68 | } catch (InterruptedException e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | list.remove(3); 73 | } 74 | }).start(); 75 | phaser.arriveAndAwaitAdvance(); 76 | 77 | System.out.println(iterator.next()); 78 | 79 | // 输出集合 80 | System.out.println(list); 81 | 82 | /** 83 | * 得到输出 84 | * 85 | * Hello 86 | * World 87 | * Java 88 | * [Hello, World, Java] 89 | */ 90 | } 91 | 92 | @Test 93 | public void updateCollectionsObject() { 94 | List list = new ArrayList<>(); 95 | Collections.addAll(list, "Hello", "World", "C++", "Java"); 96 | 97 | list.listIterator(); 98 | Iterator iterator = list.iterator(); 99 | System.out.println(iterator.next()); 100 | System.out.println(iterator.next()); 101 | 102 | // 开始操作 103 | ((List)list).set(2, new Object() { 104 | public String toString() { 105 | String s = list.get(3); 106 | list.remove(this); 107 | return s; 108 | } 109 | }); 110 | 111 | System.out.println(iterator.next()); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jdk/annotation/JdkAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jdk.annotation; 2 | 3 | /** 4 | *

5 | * Java自带的几个注解 6 | * 7 | * @Author niujinpeng 8 | * @Date 2019/4/1 13:10 9 | */ 10 | public class JdkAnnotation { 11 | 12 | /** 13 | * 抑制警告 14 | */ 15 | @SuppressWarnings(":deprecated") 16 | public void test() { 17 | deparecated(); 18 | } 19 | 20 | /** 21 | * @Deprecated 废弃方法 22 | */ 23 | @Deprecated 24 | public void deparecated() { 25 | System.out.println("废弃方法"); 26 | } 27 | 28 | /** 29 | * 方法重写 30 | * 31 | * @return 32 | */ 33 | @Override 34 | public String toString() { 35 | return "JdkAnnotation{}"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jdk/annotation/PersonAnon.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jdk.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | *

11 | * 自定义注解 12 | * 13 | * @interface 意味着实现了 java.lang.annotation.Annotation 接口 14 | * @Documented 类和方法的Annotation在缺省情况下是不出现在javadoc中的。
15 | * 如果使用@Documented修饰该Annotation, 则表示它可以出现在javadoc中。 16 | * 定义Annotation时,@Documented可有可无;若没有定义,则Annotation不会出现在javadoc中。 17 | * @Target 定义注解可以使用的位置。
18 | * 若有@Target,则该Annotation只能用于它所指定的地方;若没有@Target,则该Annotation可以用于任何地方。 19 | * @Retention 可有可无。若没有@Retention,则默认是RetentionPolicy.CLASS。 20 | * 21 | * @Author niujinpeng 22 | * @Date 2019/4/1 13:16 23 | */ 24 | @Documented 25 | @Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) // 可用位置 26 | @Retention(RetentionPolicy.RUNTIME) // RetentionPolicy.SOURCE OR RetentionPolicy.CLASS OR RetentionPolicy.RUNTIME 27 | public @interface PersonAnon { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jdk/thread/CallableFutureTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jdk.thread; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.Future; 8 | 9 | public class CallableFutureTest { 10 | @SuppressWarnings("unchecked") 11 | public static void main(String[] args) throws ExecutionException, InterruptedException { 12 | CallableFutureTest test = new CallableFutureTest(); 13 | 14 | // 创建一个线程池 15 | ExecutorService pool = Executors.newFixedThreadPool(2); 16 | // 创建两个有返回值的任务 17 | Callable c1 = test.new MyCallable("A"); 18 | Callable c2 = test.new MyCallable("B"); 19 | 20 | // 执行任务并获取Future对象 21 | Future f1 = pool.submit(c1); 22 | Future f2 = pool.submit(c2); 23 | 24 | // 从Future对象上获取任务的返回值,并输出到控制台 25 | System.out.println(">>>" + f1.get().toString()); 26 | System.out.println(">>>" + f2.get().toString()); 27 | 28 | // 关闭线程池 29 | pool.shutdown(); 30 | } 31 | 32 | @SuppressWarnings("unchecked") 33 | class MyCallable implements Callable { 34 | private String name; 35 | 36 | MyCallable(String name) { 37 | this.name = name; 38 | } 39 | 40 | @Override 41 | public Object call() throws Exception { 42 | return name + "任务返回的内容"; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jdk/thread/ExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jdk.thread; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.ThreadPoolExecutor; 6 | 7 | /** 8 | *

9 | * 10 | * @Author niujinpeng 11 | * @Date 2018/12/4 10:33 12 | */ 13 | public class ExecutorTest { 14 | 15 | public static ExecutorService executorService = Executors.newFixedThreadPool(50); 16 | 17 | 18 | public static void main(String[] args) { 19 | executorService.submit(new ThreadTest()); 20 | int threadCount = ((ThreadPoolExecutor) executorService).getActiveCount(); 21 | int poolSize = ((ThreadPoolExecutor) executorService).getPoolSize(); 22 | System.out.println("当前线程数量:" + threadCount + ",当前池中线程数:" + poolSize); 23 | } 24 | } 25 | 26 | class ThreadTest implements Runnable{ 27 | 28 | @Override 29 | public void run() { 30 | System.out.println("启动了"); 31 | while (true){ 32 | 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jvm/ConsumeCpu.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jvm; 2 | 3 | import java.util.Arrays; 4 | import java.util.Random; 5 | 6 | /** 7 | *

8 | * 消耗CPU的程序 9 | * 10 | * @Author niujinpeng 11 | * @Date 2019/3/20 18:02 12 | */ 13 | public class ConsumeCpu { 14 | 15 | public static void main(String[] args) { 16 | while (true) { 17 | int[] arr = new int[10000000]; 18 | for (int i = 0; i < 10000000; i++) { 19 | arr[i] = new Random().nextInt(10000000); 20 | } 21 | System.out.println("生成完成"); 22 | Arrays.sort(arr); 23 | System.out.println("排序完成"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jvm/DeadLock.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jvm; 2 | 3 | public class DeadLock { 4 | 5 | public static void main(String[] args) { 6 | start(); 7 | } 8 | 9 | public static void start() { 10 | new Thread(() -> { 11 | System.out.print("thread1 starting...."); 12 | synchronized (DeadLock.class) { 13 | System.out.println("Deadlock deadlock"); 14 | try { 15 | Thread.sleep(1000); 16 | } catch (InterruptedException e) { 17 | e.printStackTrace(); 18 | } 19 | synchronized (Object.class) { 20 | System.out.println("Deadlock object"); 21 | } 22 | } 23 | }).start(); 24 | 25 | new Thread(() -> { 26 | System.out.print("thread2 starting...."); 27 | synchronized (Object.class) { 28 | System.out.println("Deadlock object"); 29 | try { 30 | Thread.sleep(1000); 31 | } catch (InterruptedException e) { 32 | e.printStackTrace(); 33 | } 34 | synchronized (DeadLock.class) { 35 | System.out.println("Deadlock deadlock"); 36 | } 37 | } 38 | }).start(); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jvm/MonitoringTest.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jvm; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | 6 | /** 7 | *

8 | * 9 | * @Author niujinpeng 10 | * @Date 2018/11/26 14:15 11 | */ 12 | public class MonitoringTest { 13 | 14 | 15 | /** 16 | * 线程死循环演示 17 | */ 18 | public static void createBusyThread() { 19 | Thread thread = new Thread(new Runnable() { 20 | @Override 21 | public void run() { 22 | while (true) { 23 | 24 | } 25 | } 26 | }, "testBusyThread"); 27 | thread.start(); 28 | } 29 | 30 | /** 31 | * 线程锁等待演示 32 | * 33 | * @param lock 34 | */ 35 | public static void createLockThread(final Object lock) { 36 | Thread thread = new Thread(new Runnable() { 37 | @Override 38 | public void run() { 39 | synchronized (lock) { 40 | try { 41 | lock.wait(); 42 | } catch (InterruptedException e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | } 47 | },"testLockThread"); 48 | thread.start(); 49 | } 50 | 51 | public static void main(String[] args) throws Exception { 52 | BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 53 | br.readLine(); 54 | createBusyThread(); 55 | br.readLine(); 56 | Object obj = new Object(); 57 | createLockThread(obj); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jvm/OOMObject.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jvm; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 用于演示内存占用情况 8 | */ 9 | public class OOMObject { 10 | /** 11 | * 内存占位符对象,一个OOMObject大约占64KB 12 | */ 13 | public byte[] placeholder = new byte[64 * 1024]; 14 | 15 | public static void fillHeap(int num) throws InterruptedException { 16 | List list = new ArrayList(); 17 | for (int i = 0; i < num; i++) { 18 | // 稍作延时,令监视曲线的变化更加明显 19 | Thread.sleep(50); 20 | list.add(new OOMObject()); 21 | } 22 | System.gc(); 23 | } 24 | 25 | public static void main(String[] args) throws Exception { 26 | // fillHeap(1000); 27 | 28 | // OutOfMemoryError 29 | // List list = new ArrayList(); 30 | // while (true){ 31 | // list.add(new OOMObject()); 32 | // } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jvm/RuntimeExec.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jvm; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | 7 | /** 8 | *

9 | * 10 | * @Author niujinpeng 11 | * @Date 2018/11/26 15:17 12 | */ 13 | public class RuntimeExec { 14 | 15 | public static void main(String[] args) throws IOException { 16 | Process process = Runtime.getRuntime().exec("ipconfig"); 17 | BufferedReader bis = new BufferedReader(new InputStreamReader(process.getInputStream())); 18 | String line = null; 19 | while ((line = bis.readLine()) != null) { 20 | System.out.println(line); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jvm/StackOverflow.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jvm; 2 | 3 | /** 4 | *

5 | * 6 | * @Author niujinpeng 7 | * @Date 2019/3/22 14:23 8 | */ 9 | public class StackOverflow { 10 | public static void main(String[] args) { 11 | System.out.println(count(0)); 12 | } 13 | 14 | public static int count(int i) { 15 | System.out.println(i); 16 | return i + count(i + 1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/jvm/SynAddRunalbe.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.jvm; 2 | 3 | /** 4 | * 线程死锁等待演示 5 | */ 6 | public class SynAddRunalbe implements Runnable { 7 | int a, b; 8 | 9 | public SynAddRunalbe(int a, int b) { 10 | this.a = a; 11 | this.b = b; 12 | } 13 | 14 | @Override 15 | public void run() { 16 | synchronized (Integer.valueOf(a)) { 17 | synchronized (Integer.valueOf(b)) { 18 | System.out.println(a + b); 19 | } 20 | } 21 | } 22 | 23 | 24 | public static void main(String[] args) { 25 | for (int i = 0; i < 100; i++) { 26 | new Thread(new SynAddRunalbe(1, 2)).start(); 27 | new Thread(new SynAddRunalbe(2, 1)).start(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/other/GeneratorTextImage.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.other; 2 | 3 | 4 | import java.awt.*; 5 | import java.awt.image.BufferedImage; 6 | import java.io.File; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | 10 | import javax.imageio.ImageIO; 11 | 12 | /** 13 | *

14 | * 根据图片生成字符图案 15 | * 1.图片大小缩放 16 | * 2.遍历图片像素点 17 | * 3.获取图片像素点亮度 18 | * 4.匹配字符 19 | * 5.输出图案 20 | * 21 | * @author niujinpeng 22 | * @website www.codingme.net 23 | * @date 2019-02-25 23:03:01 24 | */ 25 | 26 | public class GeneratorTextImage { 27 | private static final char[] PIXEL = {'@', '#', '8', '&', 'o', ':', '*', '.', ' '}; 28 | 29 | public static void main(String[] args) throws Exception { 30 | // 图片缩放 31 | BufferedImage bufferedImage = makeSmallImage("java-lab/src/main/resources/banner.jpg"); 32 | // 输出 33 | printImage(bufferedImage); 34 | } 35 | 36 | public static void printImage(BufferedImage image) throws IOException { 37 | int width = image.getWidth(); 38 | int height = image.getHeight(); 39 | for (int i = 0; i < height; i++) { 40 | for (int j = 0; j < width; j++) { 41 | int rgb = image.getRGB(j, i); 42 | Color color = new Color(rgb); 43 | int red = color.getRed(); 44 | int green = color.getGreen(); 45 | int blue = color.getBlue(); 46 | // 一个用于计算RGB像素点亮度的公式 47 | Double luminace = 0.2126 * red + 0.7152 * green + 0.0722 * blue; 48 | double index = luminace / (Math.ceil(255 / PIXEL.length) + 0.5); 49 | System.out.print(PIXEL[(int)(Math.floor(index))]); 50 | } 51 | System.out.println(); 52 | } 53 | } 54 | 55 | public static BufferedImage makeSmallImage(String srcImageName) throws Exception { 56 | File srcImageFile = new File(srcImageName); 57 | if (srcImageFile == null) { 58 | System.out.println("文件不存在"); 59 | return null; 60 | } 61 | FileOutputStream fileOutputStream = null; 62 | BufferedImage tagImage = null; 63 | Image srcImage = null; 64 | try { 65 | srcImage = ImageIO.read(srcImageFile); 66 | int srcWidth = srcImage.getWidth(null);// 原图片宽度 67 | int srcHeight = srcImage.getHeight(null);// 原图片高度 68 | int dstMaxSize = 90;// 目标缩略图的最大宽度/高度,宽度与高度将按比例缩写 69 | int dstWidth = srcWidth;// 缩略图宽度 70 | int dstHeight = srcHeight;// 缩略图高度 71 | float scale = 0; 72 | // 计算缩略图的宽和高 73 | if (srcWidth > dstMaxSize) { 74 | dstWidth = dstMaxSize; 75 | scale = (float)srcWidth / (float)dstMaxSize; 76 | dstHeight = Math.round((float)srcHeight / scale); 77 | } 78 | srcHeight = dstHeight; 79 | if (srcHeight > dstMaxSize) { 80 | dstHeight = dstMaxSize; 81 | scale = (float)srcHeight / (float)dstMaxSize; 82 | dstWidth = Math.round((float)dstWidth / scale); 83 | } 84 | // 生成缩略图 85 | tagImage = new BufferedImage(dstWidth, dstHeight, BufferedImage.TYPE_INT_RGB); 86 | tagImage.getGraphics().drawImage(srcImage, 0, 0, dstWidth, dstHeight, null); 87 | return tagImage; 88 | } finally { 89 | if (fileOutputStream != null) { 90 | try { 91 | fileOutputStream.close(); 92 | } catch (Exception e) { 93 | } 94 | fileOutputStream = null; 95 | } 96 | tagImage = null; 97 | srcImage = null; 98 | System.gc(); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/rmi/Search.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.rmi; 2 | 3 | import java.rmi.Remote; 4 | import java.rmi.RemoteException; 5 | 6 | /** 7 | * 定义一个远程接口 8 | * 9 | * @author github.com/niumoo 10 | * @date 2021/04/04 11 | */ 12 | public interface Search extends Remote { 13 | User query(String userId) throws RemoteException; 14 | } 15 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/rmi/SearchClient.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.rmi; 2 | 3 | import java.rmi.Naming; 4 | 5 | /** 6 | * @author darcy 7 | * @date 2021/04/04 8 | */ 9 | public class SearchClient { 10 | public static void main(String args[]) { 11 | User answer; 12 | String value = "1"; 13 | try { 14 | // lookup method to find reference of remote object 15 | Search access = (Search)Naming.lookup("rmi://localhost:1900/search"); 16 | answer = access.query(value); 17 | System.out.println("query:" + value); 18 | System.out.println("result:" + answer); 19 | } catch (Exception ae) { 20 | System.out.println(ae); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/rmi/SearchQuery.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.rmi; 2 | 3 | import java.rmi.RemoteException; 4 | import java.rmi.server.UnicastRemoteObject; 5 | 6 | /** 7 | * @author github.com/niumoo 8 | * @date 2021/04/04 9 | */ 10 | public class SearchQuery extends UnicastRemoteObject implements Search { 11 | public SearchQuery() throws RemoteException { 12 | super(); 13 | } 14 | 15 | @Override 16 | public User query(String userId) throws RemoteException { 17 | if ("1".equals(userId)) { 18 | return new User("金庸", 100); 19 | } 20 | return new User("好汉", 20); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/rmi/SearchServer.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.rmi; 2 | 3 | import java.rmi.Naming; 4 | import java.rmi.registry.LocateRegistry; 5 | 6 | /** 7 | * @author github.com/niumoo 8 | * @date 2021/04/04 9 | */ 10 | public class SearchServer { 11 | public static void main(String[] args) { 12 | try { 13 | Search obj = new SearchQuery(); 14 | LocateRegistry.createRegistry(1900); 15 | Naming.rebind("rmi://localhost:1900/search", obj); 16 | System.out.println("start server,port is 1900"); 17 | } catch (Exception e) { 18 | e.printStackTrace(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/rmi/User.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.rmi; 2 | 3 | import java.io.Serializable; 4 | 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author niulang 11 | * @date 2021/04/04 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class User implements Serializable { 17 | 18 | private static final long serialVersionUID = -5176281163098592499L; 19 | private String name; 20 | private Integer age; 21 | } 22 | -------------------------------------------------------------------------------- /java-lab/src/main/java/com/wdbyte/lab/tree/BitTree.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.lab.tree; 2 | 3 | import java.util.Stack; 4 | 5 | import lombok.Data; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | /** 9 | *

10 | * 二叉树的遍历测试 11 | * 12 | * @Author niujinpeng 13 | * @Date 2019/4/10 10:33 14 | */ 15 | @Data 16 | @Slf4j 17 | public class BitTree { 18 | private BitTree root; 19 | private T value; 20 | private BitTree left; 21 | private BitTree right; 22 | 23 | public BitTree(T value) { 24 | this.value = value; 25 | this.left = null; 26 | this.right = null; 27 | } 28 | 29 | public BitTree(T value, BitTree left, BitTree right) { 30 | this.value = value; 31 | this.left = left; 32 | this.right = right; 33 | } 34 | 35 | /** 36 | * 前序遍历 37 | * 38 | * @param rootTree 39 | */ 40 | public void preRrder(BitTree rootTree) { 41 | if (rootTree != null) { 42 | System.out.println(rootTree.value); 43 | preRrder(rootTree.left); 44 | preRrder(rootTree.right); 45 | } 46 | } 47 | 48 | /** 49 | * 非递归前序遍历 50 | * 51 | * @param rootTree 52 | */ 53 | public void stackOrder(BitTree rootTree) { 54 | Stack stack = new Stack(); 55 | while (true) { 56 | while (rootTree != null) { 57 | System.out.println(rootTree.value); 58 | stack.push(rootTree); 59 | rootTree = rootTree.left; 60 | } 61 | if (stack.isEmpty()) { 62 | break; 63 | } 64 | BitTree bitTree = stack.pop(); 65 | rootTree = bitTree.right; 66 | } 67 | } 68 | 69 | public static void main(String[] args) { 70 | BitTree rootTree = new BitTree<>(0); 71 | rootTree.setLeft(new BitTree(1, new BitTree(2), new BitTree(3))); 72 | rootTree.setRight(new BitTree(4, new BitTree(5), new BitTree(6))); 73 | rootTree.setRoot(rootTree); 74 | // 遍历 75 | rootTree.preRrder(rootTree.root); 76 | System.out.println("---------------"); 77 | rootTree.stackOrder(rootTree.root); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /java-lab/src/main/resources/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niumoo/lab-notes/4c83a6cf055c97595f07ceb6dff83443d4dda5d5/java-lab/src/main/resources/banner.jpg -------------------------------------------------------------------------------- /java-rmi/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | com.wdbyte 8 | java-rmi 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | -------------------------------------------------------------------------------- /java-rmi/src/main/java/com/wdbyte/rmi/client/RmiClient.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.rmi.client; 2 | 3 | import java.rmi.Naming; 4 | 5 | import com.wdbyte.rmi.server.User; 6 | import com.wdbyte.rmi.server.UserService; 7 | 8 | /** 9 | * @author https://www.wdbyte.com 10 | * @date 2021/05/08 11 | */ 12 | public class RmiClient { 13 | public static void main(String args[]) { 14 | User answer; 15 | String userId = "00001"; 16 | try { 17 | // lookup method to find reference of remote object 18 | UserService access = (UserService)Naming.lookup("rmi://localhost:1900/user"); 19 | answer = access.findUser(userId); 20 | System.out.println("query:" + userId); 21 | System.out.println("result:" + answer); 22 | } catch (Exception ae) { 23 | System.out.println(ae); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /java-rmi/src/main/java/com/wdbyte/rmi/server/RmiServer.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.rmi.server; 2 | 3 | import java.rmi.Naming; 4 | import java.rmi.registry.LocateRegistry; 5 | 6 | /** 7 | * RMI Server 端 8 | * 9 | * @author https://www.wdbyte.com 10 | * @date 2021/05/08 11 | */ 12 | public class RmiServer { 13 | 14 | public static void main(String[] args) { 15 | try { 16 | UserService userService = new UserServiceImpl(); 17 | LocateRegistry.createRegistry(1900); 18 | Naming.rebind("rmi://localhost:1900/user", userService); 19 | System.out.println("start server,port is 1900"); 20 | } catch (Exception e) { 21 | e.printStackTrace(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-rmi/src/main/java/com/wdbyte/rmi/server/User.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.rmi.server; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 7 | * @author www.wdbyte.com 8 | * @date 2021/05/08 9 | */ 10 | public class User implements Serializable { 11 | 12 | private static final long serialVersionUID = 6490921832856589236L; 13 | 14 | private String name; 15 | private Integer age; 16 | private String skill; 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public Integer getAge() { 27 | return age; 28 | } 29 | 30 | public void setAge(Integer age) { 31 | this.age = age; 32 | } 33 | 34 | public String getSkill() { 35 | return skill; 36 | } 37 | 38 | public void setSkill(String skill) { 39 | this.skill = skill; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "User{" + 45 | "name='" + name + '\'' + 46 | ", age=" + age + 47 | ", skill='" + skill + '\'' + 48 | '}'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /java-rmi/src/main/java/com/wdbyte/rmi/server/UserService.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.rmi.server; 2 | 3 | import java.rmi.Remote; 4 | import java.rmi.RemoteException; 5 | 6 | 7 | /** 8 | * RMI Server 9 | * 10 | * @author www.wdbyte.com 11 | * @date 2021/05/08 12 | */ 13 | public interface UserService extends Remote { 14 | 15 | /** 16 | * 查找用户 17 | * 18 | * @param userId 19 | * @return 20 | * @throws RemoteException 21 | */ 22 | User findUser(String userId) throws RemoteException; 23 | } 24 | -------------------------------------------------------------------------------- /java-rmi/src/main/java/com/wdbyte/rmi/server/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.rmi.server; 2 | 3 | import java.rmi.RemoteException; 4 | import java.rmi.server.UnicastRemoteObject; 5 | 6 | /** 7 | * @author www.wdbyte.com 8 | * @date 2021/05/08 9 | */ 10 | public class UserServiceImpl extends UnicastRemoteObject implements UserService { 11 | 12 | protected UserServiceImpl() throws RemoteException { 13 | } 14 | 15 | @Override 16 | public User findUser(String userId) throws RemoteException { 17 | // 加载在查询 18 | if ("00001".equals(userId)) { 19 | User user = new User(); 20 | user.setName("金庸"); 21 | user.setAge(100); 22 | user.setSkill("写作"); 23 | return user; 24 | } 25 | throw new RemoteException("查无此人"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /kryo-start/src/main/java/SomeClass.java: -------------------------------------------------------------------------------- 1 | import java.io.Serializable; 2 | 3 | public class SomeClass implements Serializable { 4 | private static final long serialVersionUID = -24181467826255255L; 5 | String value; 6 | } -------------------------------------------------------------------------------- /poc-log4j2/Log4jTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niumoo/lab-notes/4c83a6cf055c97595f07ceb6dff83443d4dda5d5/poc-log4j2/Log4jTest.class -------------------------------------------------------------------------------- /poc-log4j2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.wdbyte 8 | poc-log4j2 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | 17 | 18 | 19 | org.apache.logging.log4j 20 | log4j-core 21 | 2.17.1 22 | 23 | 24 | 25 | org.apache.logging.log4j 26 | log4j-api 27 | 2.17.1 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /poc-log4j2/src/main/java/Log4j2.java: -------------------------------------------------------------------------------- 1 | import org.apache.logging.log4j.LogManager; 2 | import org.apache.logging.log4j.Logger; 3 | 4 | /** 5 | * @author www.wdbyte.com 6 | */ 7 | public class Log4j2 { 8 | private static final Logger logger = LogManager.getLogger(Log4j2.class); 9 | 10 | public static void main(String[] args) { 11 | System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true"); 12 | logger.error("params:{}","${jndi:ldap://127.0.0.1:1389/Log4jTest}"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.wdbyte 7 | lab-notes 8 | 1.0-SNAPSHOT 9 | 10 | web-arthas 11 | web-goodskill 12 | java-lab 13 | java-algorithm 14 | 15 | pom 16 | 17 | 18 | 1.8 19 | 1.8 20 | 1.8 21 | 3.8.1 22 | 1.18.4 23 | 1.2.3 24 | 29.0-jre 25 | 4.13.1 26 | 27 | 28 | 29 | 30 | 31 | org.apache.commons 32 | commons-lang3 33 | ${commons-lang3.version} 34 | 35 | 36 | 37 | 38 | org.projectlombok 39 | lombok 40 | ${lombok.version} 41 | provided 42 | 43 | 44 | 45 | ch.qos.logback 46 | logback-classic 47 | ${logback.version} 48 | 49 | 50 | 51 | 52 | com.google.guava 53 | guava 54 | ${guava.version} 55 | 56 | 57 | 58 | junit 59 | junit 60 | ${junit4.version} 61 | compile 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /web-arthas/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | lab-notes 7 | com.wdbyte 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | com.wdbyte 12 | web-arthas 13 | Arthas 使用测试案例 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-dependencies 20 | 2.1.13.RELEASE 21 | pom 22 | import 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-web 32 | 33 | 34 | spring-boot-starter-json 35 | org.springframework.boot 36 | 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-maven-plugin 52 | 2.0.4.RELEASE 53 | 54 | 55 | 56 | repackage 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /web-arthas/src/main/java/com/wdbyte/arthas/Arthas.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.arthas; 2 | 3 | import java.util.HashSet; 4 | import java.util.concurrent.ExecutorService; 5 | import java.util.concurrent.Executors; 6 | 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | /** 10 | *

11 | * Arthas Demo 12 | * 公众号:未读代码 13 | * 14 | * @Author https://www.wdbyte.com 15 | */ 16 | @Slf4j 17 | public class Arthas { 18 | 19 | private static HashSet hashSet = new HashSet(); 20 | /** 线程池,大小1*/ 21 | private static ExecutorService executorService = Executors.newFixedThreadPool(1); 22 | 23 | public static void start() { 24 | // 模拟 CPU 过高 25 | // cpu(); 26 | // 模拟线程阻塞 27 | thread(); 28 | // 模拟线程死锁 29 | deadThread(); 30 | // 不断的向 hashSet 集合增加数据 31 | addHashSetThread(); 32 | } 33 | 34 | /** 35 | * 不断的向 hashSet 集合添加数据 36 | */ 37 | public static void addHashSetThread() { 38 | // 初始化常量 39 | new Thread(() -> { 40 | int count = 0; 41 | while (true) { 42 | try { 43 | hashSet.add("count" + count); 44 | Thread.sleep(10000); 45 | count++; 46 | } catch (InterruptedException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | }).start(); 51 | } 52 | 53 | public static void cpu() { 54 | cpuHigh(); 55 | cpuNormal(); 56 | } 57 | 58 | /** 59 | * 极度消耗CPU的线程 60 | */ 61 | private static void cpuHigh() { 62 | Thread thread = new Thread(() -> { 63 | while (true) { 64 | log.info("cpu start 100"); 65 | } 66 | }); 67 | // 添加到线程 68 | executorService.submit(thread); 69 | } 70 | 71 | /** 72 | * 普通消耗CPU的线程 73 | */ 74 | private static void cpuNormal() { 75 | for (int i = 0; i < 10; i++) { 76 | new Thread(() -> { 77 | while (true) { 78 | log.info("cpu start"); 79 | try { 80 | Thread.sleep(3000); 81 | } catch (InterruptedException e) { 82 | e.printStackTrace(); 83 | } 84 | } 85 | }).start(); 86 | } 87 | } 88 | 89 | /** 90 | * 模拟线程阻塞,向已经满了的线程池提交线程 91 | */ 92 | private static void thread() { 93 | Thread thread = new Thread(() -> { 94 | while (true) { 95 | log.debug("thread start"); 96 | try { 97 | Thread.sleep(3000); 98 | } catch (InterruptedException e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | }); 103 | // 添加到线程 104 | executorService.submit(thread); 105 | } 106 | 107 | /** 108 | * 死锁 109 | */ 110 | private static void deadThread() { 111 | /** 创建资源 */ 112 | Object resourceA = new Object(); 113 | Object resourceB = new Object(); 114 | // 创建线程 115 | Thread threadA = new Thread(() -> { 116 | synchronized (resourceA) { 117 | log.info(Thread.currentThread() + " get ResourceA"); 118 | try { 119 | Thread.sleep(1000); 120 | } catch (InterruptedException e) { 121 | e.printStackTrace(); 122 | } 123 | log.info(Thread.currentThread() + "waiting get resourceB"); 124 | synchronized (resourceB) { 125 | log.info(Thread.currentThread() + " get resourceB"); 126 | } 127 | } 128 | }); 129 | 130 | Thread threadB = new Thread(() -> { 131 | synchronized (resourceB) { 132 | log.info(Thread.currentThread() + " get ResourceB"); 133 | try { 134 | Thread.sleep(1000); 135 | } catch (InterruptedException e) { 136 | e.printStackTrace(); 137 | } 138 | log.info(Thread.currentThread() + "waiting get resourceA"); 139 | synchronized (resourceA) { 140 | log.info(Thread.currentThread() + " get resourceA"); 141 | } 142 | } 143 | }); 144 | threadA.start(); 145 | threadB.start(); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /web-arthas/src/main/java/com/wdbyte/arthas/ArthasApplication.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.arthas; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | @Slf4j 9 | public class ArthasApplication { 10 | 11 | public static void main(String[] args) throws InterruptedException { 12 | SpringApplication.run(ArthasApplication.class, args); 13 | Arthas.start(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /web-arthas/src/main/java/com/wdbyte/arthas/HotCode.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.arthas; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Random; 5 | import java.util.UUID; 6 | 7 | /** 8 | *

9 | * 模拟热点代码 10 | * 11 | * @Author https://www.wdbyte.com 12 | */ 13 | public class HotCode { 14 | 15 | private static volatile int value; 16 | 17 | private static Object array; 18 | 19 | public static void main(String[] args) { 20 | while (true) { 21 | hotmethod1(); 22 | hotmethod2(); 23 | hotmethod3(); 24 | allocate(); 25 | } 26 | } 27 | 28 | /** 29 | * 生成 6万长度的数组 30 | */ 31 | private static void allocate() { 32 | array = new int[6 * 1000]; 33 | array = new Integer[6 * 1000]; 34 | } 35 | 36 | /** 37 | * 生成一个UUID 38 | */ 39 | private static void hotmethod3() { 40 | ArrayList list = new ArrayList<>(); 41 | UUID uuid = UUID.randomUUID(); 42 | String str = uuid.toString().replace("-", ""); 43 | list.add(str); 44 | } 45 | 46 | /** 47 | * 数字累加 48 | */ 49 | private static void hotmethod2() { 50 | value++; 51 | } 52 | 53 | /** 54 | * 生成一个随机数 55 | */ 56 | private static void hotmethod1() { 57 | Random random = new Random(); 58 | int anInt = random.nextInt(); 59 | } 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /web-arthas/src/main/java/com/wdbyte/arthas/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.arthas.controller; 2 | 3 | import java.util.HashMap; 4 | 5 | import com.wdbyte.arthas.service.UserServiceImpl; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | /** 12 | *

13 | * 14 | * @Author https://www.wdbyte.com 15 | */ 16 | @RestController 17 | @Slf4j 18 | public class UserController { 19 | 20 | @Autowired 21 | private UserServiceImpl userService; 22 | 23 | @GetMapping(value = "/user") 24 | public HashMap getUser(Integer uid) throws Exception { 25 | // 模拟用户查询 26 | userService.get(uid); 27 | HashMap hashMap = new HashMap<>(); 28 | hashMap.put("uid", uid); 29 | hashMap.put("name", "name" + uid); 30 | return hashMap; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /web-arthas/src/main/java/com/wdbyte/arthas/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.arthas.service; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.stereotype.Service; 5 | 6 | /** 7 | *

8 | * 9 | * @Author https://www.wdbyte.com 10 | */ 11 | @Service 12 | @Slf4j 13 | public class UserServiceImpl { 14 | 15 | public void get(Integer uid) throws Exception { 16 | check(uid); 17 | service(uid); 18 | redis(uid); 19 | mysql(uid); 20 | } 21 | 22 | public void service(Integer uid) throws Exception { 23 | int count = 0; 24 | for (int i = 0; i < 10; i++) { 25 | count += i; 26 | } 27 | log.info("com.wdbyte.goodskill.service end {}", count); 28 | } 29 | 30 | public void redis(Integer uid) throws Exception { 31 | int count = 0; 32 | for (int i = 0; i < 10000; i++) { 33 | count += i; 34 | } 35 | log.info("redis end {}", count); 36 | } 37 | 38 | public void mysql(Integer uid) throws Exception { 39 | long count = 0; 40 | for (int i = 0; i < 10000000; i++) { 41 | count += i; 42 | } 43 | log.info("mysql end {}", count); 44 | } 45 | 46 | public boolean check(Integer uid) throws Exception { 47 | if (uid == null || uid < 0) { 48 | log.error("uid不正确,uid:{}", uid); 49 | throw new Exception("uid不正确"); 50 | } 51 | return true; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /web-arthas/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | servlet: 4 | context-path: / 5 | -------------------------------------------------------------------------------- /web-arthas/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}: %msg%n 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ${catalina.base}/logs/logback-info.log.%d{yyyy-MM-dd} 20 | 21 | 60 22 | 23 | 24 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}: %msg%n 25 | 26 | 27 | INFO 28 | ACCEPT 29 | DENY 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ${catalina.base}/logs/logback-error.log.%d{yyyy-MM-dd} 39 | 40 | 60 41 | 42 | 43 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}: %msg%n 44 | 45 | 46 | ERROR 47 | ACCEPT 48 | DENY 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /web-goodskill/README.md: -------------------------------------------------------------------------------- 1 | ## 简单的秒杀系统的实现 2 | 3 | ### 知识点 4 | - 唯一主键 5 | - Redis 分布式锁 6 | - 秒杀商品缓存 7 | - 用户订单异步处理 -------------------------------------------------------------------------------- /web-goodskill/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | lab-notes 7 | com.wdbyte 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | web-goodskill 12 | 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-dependencies 18 | 2.1.13.RELEASE 19 | pom 20 | import 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | spring-boot-starter-json 33 | org.springframework.boot 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-test 41 | test 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-data-redis 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-maven-plugin 54 | 2.0.4.RELEASE 55 | 56 | 57 | 58 | repackage 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /web-goodskill/src/main/java/README.md: -------------------------------------------------------------------------------- 1 | ## 简单的秒杀系统的实现 2 | 3 | ### 知识点 4 | - 唯一主键 5 | - Redis 分布式锁 6 | - 秒杀商品缓存 7 | - 用户订单异步处理 -------------------------------------------------------------------------------- /web-goodskill/src/main/java/com/wdbyte/goodskill/BootApplication.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.goodskill; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * Springboot 启动 8 | * 9 | * @author https://www.wdbyte.com 10 | */ 11 | @SpringBootApplication 12 | public class BootApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(BootApplication.class, args); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /web-goodskill/src/main/java/com/wdbyte/goodskill/controller/SkillController.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.goodskill.controller; 2 | 3 | import com.wdbyte.goodskill.service.SkillService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | /** 9 | *

10 | * 简单的商品秒杀 11 | *

12 | * 13 | * @Author https://www.wdbyte.com 14 | * @Date 2019/3/20 11:33 15 | */ 16 | @RestController 17 | public class SkillController { 18 | 19 | @Autowired 20 | private SkillService skillService; 21 | 22 | @GetMapping(value = "/sell/skill") 23 | public String skill(String product) { 24 | return skillService.skillProduct(product); 25 | } 26 | 27 | @GetMapping(value = "/sell/skill-redis") 28 | public String skillRedis(String product) { 29 | return skillService.skillProductByRedis(product); 30 | } 31 | 32 | @GetMapping(value = "/sell/skill/query") 33 | public String querySkillProductInfo(String product) { 34 | return skillService.querySkillProductInfo(product); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /web-goodskill/src/main/java/com/wdbyte/goodskill/service/SkillService.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.goodskill.service; 2 | 3 | /** 4 | *

5 | * 6 | * @Author https://www.wdbyte.com 7 | * @Date 2019/3/2011:36 8 | */ 9 | public interface SkillService { 10 | 11 | String querySkillProductInfo(String productId); 12 | 13 | String skillProduct(String productId); 14 | 15 | String skillProductByRedis(String productId); 16 | } 17 | -------------------------------------------------------------------------------- /web-goodskill/src/main/java/com/wdbyte/goodskill/service/impl/SkillServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.goodskill.service.impl; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.wdbyte.goodskill.service.SkillService; 7 | import com.wdbyte.goodskill.utils.KeyUtil; 8 | import com.wdbyte.goodskill.utils.RedisLock; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | /** 13 | *

14 | * 15 | * @Author https://www.wdbyte.com 16 | * @Date 2019/3/20 11:36 17 | */ 18 | @Service 19 | public class SkillServiceImpl implements SkillService { 20 | 21 | @Autowired 22 | private RedisLock redisLock; 23 | 24 | private static Integer TIME_OUT = 10 * 1000; 25 | 26 | /** 商品和抢购数量 */ 27 | static Map products; 28 | /** 库存 */ 29 | static Map stock; 30 | /** 订单 */ 31 | static Map orders; 32 | static { 33 | products = new HashMap<>(); 34 | stock = new HashMap<>(); 35 | orders = new HashMap<>(); 36 | products.put("iphonex", 10000); 37 | stock.put("iphonex", 10000); 38 | } 39 | 40 | public String getProductInfo(String product) { 41 | return " iphonex 抢购活动火爆进行中,限量:" + products.get(product) + "份,目前剩余" + stock.get(product) + "份,下单人数:" 42 | + orders.size() + "人!"; 43 | } 44 | 45 | @Override 46 | public String querySkillProductInfo(String product) { 47 | return getProductInfo(product); 48 | } 49 | 50 | @Override 51 | public String skillProduct(String product) { 52 | // 查询库存 53 | if (stock.get(product) == 0) { 54 | return "活动已经结束!"; 55 | } 56 | // 下单 57 | orders.put(KeyUtil.getUniqueKey(), product); 58 | // 扣库存 59 | try { 60 | // 模拟时间消耗 61 | Thread.sleep(10); 62 | } catch (InterruptedException e) { 63 | e.printStackTrace(); 64 | } 65 | stock.put(product, stock.get(product) - 1); 66 | return "恭喜你强到了 " + product; 67 | } 68 | 69 | @Override 70 | public String skillProductByRedis(String product) { 71 | long value = System.currentTimeMillis() + TIME_OUT; 72 | if (!redisLock.lock(product, String.valueOf(value))) { 73 | return "当前人数过于火爆"; 74 | } 75 | // 查询库存 76 | if (stock.get(product) == 0) { 77 | return "活动已经结束!"; 78 | } 79 | // 下单 80 | orders.put(KeyUtil.getUniqueKey(), product); 81 | // 扣库存 82 | try { 83 | // 模拟时间消耗 84 | Thread.sleep(10); 85 | } catch (InterruptedException e) { 86 | e.printStackTrace(); 87 | } 88 | stock.put(product, stock.get(product) - 1); 89 | redisLock.unlock(product, String.valueOf(value)); 90 | return "恭喜你强到了 " + product; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /web-goodskill/src/main/java/com/wdbyte/goodskill/utils/KeyUtil.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.goodskill.utils; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.Random; 6 | import java.util.UUID; 7 | 8 | /** 9 | *

10 | * 11 | * 12 | * @Author https://www.wdbyte.com 13 | * @Date 2019/1/31 17:28 14 | */ 15 | public class KeyUtil { 16 | 17 | private static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); 18 | /** 19 | * 生成唯一主键 格式:时间+随机数 20 | * 21 | * @return 22 | */ 23 | public static synchronized String getUniqueKey() { 24 | Random random = new Random(); 25 | Integer randNumber = random.nextInt(900000) + 100000; 26 | return sdf.format(new Date()) + String.valueOf(randNumber); 27 | } 28 | 29 | public static void main(String[] args) { 30 | long start = System.currentTimeMillis(); 31 | for (int i = 0; i < 1000000; i++) { 32 | System.out.println(UUID.randomUUID()); 33 | } 34 | long end = System.currentTimeMillis(); 35 | System.out.println(end-start); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /web-goodskill/src/main/java/com/wdbyte/goodskill/utils/RedisLock.java: -------------------------------------------------------------------------------- 1 | package com.wdbyte.goodskill.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | *

11 | * Redis 分布式锁的实现 12 | * 13 | * @Author https://www.wdbyte.com 14 | * @Date 2019/3/20 14:54 15 | */ 16 | @Slf4j 17 | @Component 18 | public class RedisLock { 19 | 20 | @Autowired 21 | private RedisTemplate redisTemplate; 22 | 23 | public boolean lock(String key, String value) { 24 | // 没有锁直接加锁 25 | if (redisTemplate.opsForValue().setIfAbsent(key, value)) { 26 | return true; 27 | } 28 | String currentValue = (String)redisTemplate.opsForValue().get(key); 29 | // 如果锁过期 30 | if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) { 31 | // 获取上一个锁的时间 32 | String oldValue = (String)redisTemplate.opsForValue().getAndSet(key, value); 33 | if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) { 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | public void unlock(String key, String value) { 41 | try { 42 | String currentValue = (String)redisTemplate.opsForValue().get(key); 43 | if (StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)) { 44 | redisTemplate.opsForValue().getOperations().delete(key); 45 | } 46 | } catch (Exception e) { 47 | log.error("【redis分布式锁】解锁异常"); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /web-goodskill/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | servlet: 4 | context-path: / 5 | 6 | spring: 7 | redis: 8 | database: 0 9 | host: 127.0.0.1 10 | port: 6379 11 | password: 12 | timeout: 10 13 | jedis: 14 | pool: 15 | max-active: 9 16 | max-wait: -1 17 | max-idle: 8 18 | min-idle: 0 19 | -------------------------------------------------------------------------------- /web-goodskill/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}: %msg%n 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ${catalina.base}/logs/logback-info.log.%d{yyyy-MM-dd} 20 | 21 | 60 22 | 23 | 24 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}: %msg%n 25 | 26 | 27 | INFO 28 | ACCEPT 29 | DENY 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ${catalina.base}/logs/logback-error.log.%d{yyyy-MM-dd} 39 | 40 | 60 41 | 42 | 43 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}: %msg%n 44 | 45 | 46 | ERROR 47 | ACCEPT 48 | DENY 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | --------------------------------------------------------------------------------